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(
'audio/(:num)/(:num)/(:num)/(:num)/(:any)',
'Analytics::hit/$1/$2/$3/$4/$5',
'audio/(:num)/(:num)/(:num)/(:num)/(:num)/(:any)',
'Analytics::hit/$1/$2/$3/$4/$5/$6',
[
'as' => 'analytics_hit',
]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,26 +18,24 @@ class AddAnalyticsPodcastsByEpisode extends Migration
{
$this->forge->addField([
'podcast_id' => [
'type' => 'BIGINT',
'constraint' => 20,
'type' => 'INT',
'unsigned' => true,
],
'date' => [
'type' => 'date',
'type' => 'DATE',
],
'episode_id' => [
'type' => 'BIGINT',
'constraint' => 20,
'type' => 'INT',
'unsigned' => true,
],
'age' => [
'type' => 'INT',
'constraint' => 10,
'comment' => 'Days since episode publication date',
'unsigned' => true,
],
'hits' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => true,
'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([
'podcast_id' => [
'type' => 'BIGINT',
'constraint' => 20,
'type' => 'INT',
'unsigned' => true,
],
'date' => [
'type' => 'date',
'type' => 'DATE',
],
'service' => [
'type' => 'VARCHAR',
@ -41,14 +40,14 @@ class AddAnalyticsPodcastsByPlayer extends Migration
'type' => 'VARCHAR',
'constraint' => 32,
],
'bot' => [
'is_bot' => [
'type' => 'TINYINT',
'constraint' => 1,
'default' => 0,
],
'hits' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => true,
'default' => 1,
],
]);
@ -59,7 +58,7 @@ class AddAnalyticsPodcastsByPlayer extends Migration
'app',
'device',
'os',
'bot',
'is_bot',
]);
$this->forge->addField(
'`created_at` timestamp NOT NULL DEFAULT current_timestamp()'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,8 +18,7 @@ class AddAnalyticsUnknownUseragents extends Migration
{
$this->forge->addField([
'id' => [
'type' => 'BIGINT',
'constraint' => 20,
'type' => 'INT',
'unsigned' => true,
'auto_increment' => true,
],
@ -30,12 +29,12 @@ class AddAnalyticsUnknownUseragents extends Migration
],
'hits' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => true,
'default' => 1,
],
]);
$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(
'`created_at` timestamp NOT NULL DEFAULT current_timestamp()'
);

View File

@ -1,8 +1,8 @@
<?php
/**
* Class AddAnalyticsPodcastsStoredProcedure
* Creates analytics_podcasts stored procedure in database
* Class AddAnalyticsPodcastsProcedure
* Creates analytics_podcasts procedure in database
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
@ -12,18 +12,18 @@ namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddAnalyticsPodcastsStoredProcedure extends Migration
class AddAnalyticsPodcastsProcedure extends Migration
{
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);
$prefix = $this->db->getPrefix();
$createQuery = <<<EOD
CREATE PROCEDURE `{$prefix}analytics_podcasts` (
IN `p_podcast_id` BIGINT(20) UNSIGNED,
IN `p_episode_id` BIGINT(20) UNSIGNED,
IN `p_podcast_id` INT UNSIGNED,
IN `p_episode_id` INT UNSIGNED,
IN `p_country_code` VARCHAR(3) CHARSET utf8mb4,
IN `p_region_code` VARCHAR(3) CHARSET utf8mb4,
IN `p_latitude` FLOAT,
@ -33,28 +33,42 @@ CREATE PROCEDURE `{$prefix}analytics_podcasts` (
IN `p_device` VARCHAR(32) CHARSET utf8mb4,
IN `p_os` VARCHAR(32) CHARSET utf8mb4,
IN `p_bot` TINYINT(1) UNSIGNED,
IN `p_filesize` INT UNSIGNED,
IN `p_duration` INT UNSIGNED,
IN `p_new_listener` TINYINT(1) UNSIGNED
) MODIFIES SQL DATA
DETERMINISTIC
SQL SECURITY INVOKER
COMMENT 'Add one hit in podcast logs tables.'
BEGIN
SET @current_datetime = NOW();
SET @current_date = DATE(@current_datetime);
SET @current_hour = HOUR(@current_datetime);
IF NOT `p_bot` THEN
INSERT INTO `{$prefix}analytics_podcasts`(`podcast_id`, `date`)
VALUES (p_podcast_id, DATE(NOW()))
ON DUPLICATE KEY UPDATE `hits`=`hits`+1, `unique_listeners`=`unique_listeners`+`p_new_listener`;
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
INSERT INTO `{$prefix}analytics_podcasts`(`podcast_id`, `date`)
VALUES (p_podcast_id, @current_date)
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`)
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;
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;
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;
END IF;
INSERT INTO `{$prefix}analytics_podcasts_by_player`(`podcast_id`, `service`, `app`, `device`, `os`, `bot`, `date`)
VALUES (p_podcast_id, p_service, p_app, p_device, p_os, p_bot, DATE(NOW()))
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, @current_date)
ON DUPLICATE KEY UPDATE `hits`=`hits`+1;
END
EOD;

View File

@ -1,8 +1,8 @@
<?php
/**
* Class AddAnalyticsUnknownUseragentsStoredProcedure
* Creates analytics_unknown_useragents stored procedure in database
* Class AddAnalyticsUnknownUseragentsProcedure
* Creates analytics_unknown_useragents procedure in database
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
@ -12,11 +12,11 @@ namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddAnalyticsUnknownUseragentsStoredProcedure extends Migration
class AddAnalyticsUnknownUseragentsProcedure extends Migration
{
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');
$procedureName = $this->db->prefixTable('analytics_unknown_useragents');
$createQuery = <<<EOD

View File

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

View File

@ -13,24 +13,21 @@ namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddUsersPodcasts extends Migration
class AddPodcastsUsers extends Migration
{
public function up()
{
$this->forge->addField([
'user_id' => [
'podcast_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
'podcast_id' => [
'type' => 'BIGINT',
'constraint' => 20,
'user_id' => [
'type' => 'INT',
'unsigned' => true,
],
'group_id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
],
]);
@ -38,11 +35,11 @@ class AddUsersPodcasts extends Migration
$this->forge->addForeignKey('user_id', 'users', 'id');
$this->forge->addForeignKey('podcast_id', 'podcasts', 'id');
$this->forge->addForeignKey('group_id', 'auth_groups', 'id');
$this->forge->createTable('users_podcasts');
$this->forge->createTable('podcasts_users');
}
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([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'title' => [
'type' => 'VARCHAR',
'constraint' => 1024,
'constraint' => 255,
],
'slug' => [
'type' => 'VARCHAR',
@ -37,10 +36,10 @@ class AddPages extends Migration
'type' => 'TEXT',
],
'created_at' => [
'type' => 'TIMESTAMP',
'type' => 'DATETIME',
],
'updated_at' => [
'type' => 'TIMESTAMP',
'type' => 'DATETIME',
],
'deleted_at' => [
'type' => 'DATETIME',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,7 +17,9 @@ class AnalyticsPodcasts extends Entity
protected $casts = [
'podcast_id' => 'integer',
'date' => 'datetime',
'hits' => 'integer',
'duration' => 'integer',
'bandwidth' => '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',
'device' => '?string',
'os' => '?string',
'bot' => 'boolean',
'is_bot' => 'boolean',
'date' => 'datetime',
'hits' => 'integer',
];

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -65,7 +65,7 @@ return [
'status_section_subtitle' => 'Dead or alive?',
'block' => 'Podcast should be hidden from all platforms',
'complete' => 'Podcast will not be having new episodes',
'lock' => 'Podcast is locked for export',
'lock' => 'Prevent podcast from being copied',
'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.',
'submit_create' => 'Create podcast',

View File

@ -66,7 +66,7 @@ return [
'status_section_subtitle' => 'Vivant ou mort?',
'block' => 'Le podcast doit être masqué sur toutes les plateformes',
'complete' => 'Le podcast naura plus de nouveaux épisodes.',
'lock' => 'Le podcast est fermé à lexport',
'lock' => 'Empêcher la copie du podcast',
'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.',
'submit_create' => 'Créer le podcast',

View File

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

View File

@ -138,4 +138,69 @@ class AnalyticsPodcastModel extends Model
}
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"))) {
$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`')
->where([

View File

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

View File

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

View File

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

View File

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

View File

@ -16,12 +16,12 @@ class UserModel extends \Myth\Auth\Models\UserModel
{
if (!($found = cache("podcast{$podcastId}_contributors"))) {
$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(
'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();
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)
{
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('auth_groups', 'auth_groups.id = users_podcasts.group_id')
->join('podcasts_users', 'podcasts_users.user_id = users.id')
->join('auth_groups', 'auth_groups.id = podcasts_users.group_id')
->where([
'users.id' => $user_id,
'podcast_id' => $podcast_id,

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,7 @@
<div class="relative flex items-start mb-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">
<?= anchor($platform->home_url, icon('external-link', 'mx-auto'), [
'class' => 'flex-1 text-gray-600 hover:text-gray-900',
@ -81,7 +81,7 @@
'yes',
old(
$platform->name . '_visible',
$platform->visible ? $platform->visible : false
$platform->is_visible ? $platform->is_visible : false
),
'text-sm'
) ?>

View File

@ -5,9 +5,7 @@
<head>
<meta charset="UTF-8"/>
<title><?= $episode->title ?></title>
<meta name="description" content="<?= trim(
preg_replace('/\s+/', ' ', strip_tags($episode->description_html))
) ?>"/>
<meta name="description" content="<?= $episode->description ?>"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
<link rel="stylesheet" href="/assets/index.css"/>

View File

@ -6,9 +6,7 @@
<head>
<meta charset="UTF-8"/>
<title><?= $podcast->title ?></title>
<meta name="description" content="<?= trim(
preg_replace('/\s+/', ' ', strip_tags($podcast->description_html))
) ?>"/>
<meta name="description" content="<?= $podcast->description ?>"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
<link rel="stylesheet" href="/assets/index.css"/>
@ -46,12 +44,9 @@
]
) ?>
<?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">
<?= platform_icon(
$platform->icon_filename,
'h-8'
) ?>
<?= platform_icon($platform->name, 'h-8') ?>
</a>
<?php endif; ?>
<?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
# 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
mv ./writable/uploads/GeoLite2-City* ./writable/uploads/GeoLite2-City