first commit
Some checks failed
Build / run (push) Has been cancelled

This commit is contained in:
maher
2025-10-29 11:42:25 +01:00
commit 703f50a09d
4595 changed files with 385164 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Common\Auth\Permissions\Permission;
use Common\Auth\Permissions\Traits\SyncsPermissions;
use Common\Database\Seeds\DefaultPagesSeeder;
use Common\Localizations\Localization;
use Common\Pages\CustomPage;
use Hash;
use Illuminate\Console\Command;
class CleanDemoSite extends Command
{
use SyncsPermissions;
protected $signature = 'demo:clean';
public function handle(): void
{
// reset admin user
$this->cleanAdminUser('admin@admin.com');
// delete localizations
Localization::get()->each(function (Localization $localization) {
if (strtolower($localization->name) !== 'english') {
$localization->delete();
}
});
// delete custom pages
CustomPage::truncate();
app(DefaultPagesSeeder::class)->run();
}
private function cleanAdminUser($email): void
{
$admin = User::where('email', $email)->first();
if (!$admin) {
$admin = User::create([
'email' => $email,
]);
}
$admin->avatar = null;
$admin->username = 'admin';
$admin->first_name = 'Demo';
$admin->last_name = 'Admin';
$admin->password = 'admin';
$admin->email_verified_at = now()->subDays(10);
$admin->save();
$adminPermission = app(Permission::class)
->where('name', 'admin')
->first();
$this->syncPermissions($admin, [$adminPermission]);
}
}

View File

@@ -0,0 +1,269 @@
<?php
namespace App\Console\Commands;
use App\Actions\Demo\GenerateDemoAnimeVideos;
use App\Actions\Demo\GenerateDemoComments;
use App\Actions\Demo\GenerateDemoReviews;
use App\Actions\Demo\GenerateDemoStreamVideos;
use App\Actions\Demo\GenerateDemoUsers;
use App\Actions\Demo\GenerateDemoVideoVotes;
use App\Models\Channel;
use Common\Admin\Appearance\Themes\CssTheme;
use Common\Channels\GenerateChannelsFromConfig;
use Common\Channels\UpdateAllChannelsContent;
use Common\Settings\DotEnvEditor;
use Common\Settings\Setting;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
class GenerateDemoDataCommand extends Command
{
protected $signature = 'demo:generate {variant} {--truncate}';
public function handle(): void
{
@set_time_limit(0);
@ini_set('memory_limit', '200M');
Artisan::call('down');
$variant = $this->argument('variant');
if ($variant === 'database') {
$this->databaseVariant();
} elseif ($variant === 'streaming') {
$this->streamingVariant();
} elseif ($variant === 'anime') {
$this->animeVariant();
} else {
$this->error('Invalid variant');
}
$this->info('Demo data generated', true);
Artisan::call('up');
}
protected function databaseVariant(): void
{
$this->overridePrimaryMenu([
[
'id' => 'cVKgbI',
'type' => 'channels',
'label' => 'Movies',
'action' => '/movies',
],
[
'id' => 'nVKg4v',
'type' => 'channels',
'label' => 'Series',
'action' => '/series',
],
[
'id' => 'nVKg1Ix',
'type' => 'channels',
'label' => 'People',
'action' => '/people',
],
[
'id' => 'nVKxdI',
'type' => 'channels',
'label' => 'News',
'action' => '/latest-news',
],
]);
$homepageChannel = $this->generateChannels([
resource_path('defaults/channels/shared-channels.json'),
resource_path('defaults/channels/default-channels.json'),
]);
settings()->save([
'homepage.type' => 'channels',
'homepage.value' => $homepageChannel->id,
'streaming.video_panel_content' => 'all',
'streaming.prefer_full' => false,
'streaming.show_video_selector' => false,
'streaming.show_header_play' => true,
'content.search_provider' => 'tmdb',
'content.title_provider' => 'tmdb',
'content.people_provider' => 'tmdb',
'content.automate_filmography' => true,
'title_page.sections' => json_encode([
'seasons',
'videos',
'images',
'reviews',
'cast',
'related',
]),
]);
Artisan::call(UpdateAllChannelsContent::class);
(new GenerateDemoUsers())->execute();
(new GenerateDemoVideoVotes())->execute();
(new GenerateDemoComments())->execute();
(new GenerateDemoReviews())->execute();
}
protected function streamingVariant(): void
{
if ($this->option('truncate')) {
Artisan::call(TruncateTitleData::class);
}
$this->overridePrimaryMenu([
[
'id' => 'cVKg0I',
'type' => 'route',
'label' => 'Movies',
'action' => '/movies',
],
[
'id' => 'nVKg0v',
'type' => 'route',
'label' => 'TV shows',
'action' => '/series',
],
[
'id' => 'nVKg0Ix',
'type' => 'route',
'label' => 'Watchlist',
'action' => '/watchlist',
],
]);
$homepageChannel = $this->generateChannels([
resource_path('defaults/channels/shared-channels.json'),
resource_path('defaults/channels/streaming-channels.json'),
]);
$darkTheme = CssTheme::where('default_dark', true)->first();
settings()->save([
'themes.default_id' => $darkTheme->id,
'homepage.type' => 'landingPage',
'homepage.value' => $homepageChannel->id,
'streaming.video_panel_content' => 'full',
'streaming.prefer_full' => true,
'streaming.show_video_selector' => false,
'streaming.show_header_play' => true,
'content.search_provider' => 'local',
'content.title_provider' => 'tmdb',
'content.people_provider' => 'tmdb',
'content.automate_filmography' => false,
'title_page.sections' => json_encode([
'seasons',
'images',
'reviews',
'cast',
'related',
]),
]);
app(DotEnvEditor::class)->write([
'scout_driver' => 'meilisearch',
]);
$this->info('Updating channels');
Artisan::call(UpdateAllChannelsContent::class);
$this->info('Channels updated.');
$this->info('Updating seasons');
Artisan::call(UpdateSeasonsFromRemote::class);
$this->info('Seasons updated.');
(new GenerateDemoUsers())->execute();
(new GenerateDemoStreamVideos())->execute();
(new GenerateDemoVideoVotes())->execute();
(new GenerateDemoComments())->execute();
(new GenerateDemoReviews())->execute();
}
protected function animeVariant(): void
{
if ($this->option('truncate')) {
Artisan::call(TruncateTitleData::class);
}
$this->overridePrimaryMenu([
[
'id' => 'cVKg0I',
'type' => 'route',
'label' => 'Movies',
'action' => '/movies',
],
[
'id' => 'nVKg0v',
'type' => 'route',
'label' => 'TV shows',
'action' => '/series',
],
[
'id' => 'nVKg0Ix',
'type' => 'route',
'label' => 'Watchlist',
'action' => '/watchlist',
],
]);
$homepageChannel = $this->generateChannels([
resource_path('defaults/channels/shared-channels.json'),
resource_path('defaults/channels/anime-channels.json'),
]);
settings()->save([
'homepage.type' => 'channels',
'homepage.value' => $homepageChannel->id,
'streaming.video_panel_content' => 'full',
'streaming.prefer_full' => true,
'streaming.show_video_selector' => true,
'streaming.show_header_play' => true,
'content.search_provider' => 'local',
'content.title_provider' => 'tmdb',
'content.people_provider' => 'tmdb',
'content.automate_filmography' => false,
'title_page.sections' => json_encode([
'episodes',
'images',
'reviews',
'cast',
'related',
]),
]);
app(DotEnvEditor::class)->write([
'scout_driver' => 'meilisearch',
]);
Artisan::call(UpdateAllChannelsContent::class);
(new GenerateDemoUsers())->execute();
(new GenerateDemoAnimeVideos())->execute();
(new GenerateDemoVideoVotes())->execute();
(new GenerateDemoComments())->execute('anime');
(new GenerateDemoReviews())->execute();
}
protected function overridePrimaryMenu(array $items): void
{
$menus = Setting::where('name', 'menus')->first()->value;
$index = array_search('primary', array_column($menus, 'name'));
$menus[$index]['items'] = $items;
Setting::where('name', 'menus')->update([
'value' => json_encode($menus),
]);
}
protected function generateChannels(array $paths): ?Channel
{
$ids = Channel::where('type', 'channel')->pluck('id');
DB::table('channelables')
->whereIn('channel_id', $ids)
->delete();
Channel::whereIn('id', $ids)->delete();
return (new GenerateChannelsFromConfig())->execute($paths);
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace App\Console\Commands;
use App\Services\SitemapGenerator;
use Illuminate\Console\Command;
class GenerateSitemap extends Command
{
protected $signature = 'sitemap:generate';
protected $description = 'Generate sitemaps for all site resources.';
public function handle(): void
{
app(SitemapGenerator::class)->generate();
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace App\Console\Commands;
use App\Services\Data\Tmdb\TmdbApi;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
class SyncTmdbValueLists extends Command
{
protected $signature = 'tmdb:syncValueLists';
public function handle(): int
{
$this->syncGenres();
$this->syncCountries();
$this->syncDepartments();
$this->syncLanguages();
$this->syncKeywords();
return Command::SUCCESS;
}
protected function syncKeywords(): void
{
$downloadUrl =
'http://files.tmdb.org/p/exports/keyword_ids_07_08_2023.json.gz';
$content = gzdecode(file_get_contents($downloadUrl));
$content = str_replace('"}', '"},', $content);
$content = rtrim(trim($content), ',');
file_put_contents(resource_path('lists/tmdb-keywords.json'), '[' . $content . ']');
}
protected function syncGenres(): void
{
$movieGenres = Http::get(TmdbApi::TMDB_BASE . 'genre/movie/list', [
'api_key' => config('services.tmdb.key'),
'language' => 'en-US',
])->json()['genres'];
$tvGenres = Http::get(TmdbApi::TMDB_BASE . 'genre/tv/list', [
'api_key' => config('services.tmdb.key'),
'language' => 'en-US',
])->json()['genres'];
$mergedGenres = collect($movieGenres)
->merge($tvGenres)
->unique('id')
->values()
->toArray();
file_put_contents(
resource_path('lists/tmdb-genres.json'),
json_encode($mergedGenres),
);
}
private function syncLanguages(): void
{
$languages = Http::get(TmdbApi::TMDB_BASE . 'configuration/languages', [
'api_key' => config('services.tmdb.key'),
])->json();
$languages = array_map(fn($language) => [
'code' => $language['iso_639_1'],
'name' => $language['english_name'],
], $languages);
file_put_contents(
resource_path('lists/tmdb-languages.json'),
json_encode($languages),
);
}
protected function syncDepartments(): void
{
$departments = Http::get(TmdbApi::TMDB_BASE . 'configuration/jobs', [
'api_key' => config('services.tmdb.key'),
])->json();
file_put_contents(
resource_path('lists/tmdb-departments.json'),
json_encode($departments),
);
}
protected function syncCountries(): void
{
$countries = Http::get(TmdbApi::TMDB_BASE . 'configuration/countries', [
'api_key' => config('services.tmdb.key'),
])->json();
$countries = array_map(fn($country) => [
'code' => $country['iso_3166_1'],
'name' => $country['english_name'],
], $countries);
file_put_contents(
resource_path('lists/tmdb-countries.json'),
json_encode($countries),
);
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Console\Commands;
use DB;
use Illuminate\Console\Command;
class TruncateTitleData extends Command
{
protected $signature = 'titles:truncate';
protected $description = 'Truncate all title related database tables.';
public function handle()
{
DB::table('channelables')->truncate();
DB::table('channels')->truncate();
DB::table('comment_reports')->truncate();
DB::table('comment_votes')->truncate();
DB::table('comments')->truncate();
DB::table('country_title')->truncate();
DB::table('creditables')->truncate();
DB::table('episodes')->truncate();
DB::table('genre_title')->truncate();
DB::table('genres')->truncate();
DB::table('images')->truncate();
DB::table('keyword_title')->truncate();
DB::table('keywords')->truncate();
DB::table('production_countries')->truncate();
DB::table('review_feedback')->truncate();
DB::table('review_reports')->truncate();
DB::table('reviews')->truncate();
DB::table('seasons')->truncate();
DB::table('tags')->truncate();
DB::table('taggables')->truncate();
DB::table('people')->truncate();
DB::table('titles')->truncate();
DB::table('videos')->truncate();
DB::table('video_captions')->truncate();
DB::table('video_reports')->truncate();
DB::table('video_plays')->truncate();
DB::table('video_votes')->truncate();
DB::table('news_article_models')->truncate();
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Console\Commands;
use App\Actions\News\ImportNewsFromRemoteProvider;
use Illuminate\Console\Command;
class UpdateNewsFromRemote extends Command
{
protected $signature = 'news:update';
protected $description = 'Update news from currently selected 3rd party site.';
public function handle(): void
{
app(ImportNewsFromRemoteProvider::class)->execute();
$this->info('News updated.');
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Console\Commands;
use App\Models\Season;
use Illuminate\Console\Command;
class UpdateSeasonsFromRemote extends Command
{
protected $signature = 'seasons:update';
public function handle(): void
{
$seasons = Season::orderBy('updated_at', 'asc')
->with('title')
->limit(50)
->get();
$this->withProgressBar($seasons, function (Season $season) {
if ($season->title) {
$season->maybeUpdateFromExternal($season->title);
}
});
$this->info('Seasons updated');
}
}

45
app/Console/Kernel.php Executable file
View File

@@ -0,0 +1,45 @@
<?php
namespace App\Console;
use App\Console\Commands\CleanDemoSite;
use App\Console\Commands\UpdateNewsFromRemote;
use App\Console\Commands\UpdateSeasonsFromRemote;
use Common\Channels\UpdateAllChannelsContent;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
protected $commands = [UpdateAllChannelsContent::class];
protected function schedule(Schedule $schedule): void
{
if (settings('news.auto_update')) {
$schedule->command(UpdateNewsFromRemote::class)->daily();
}
if (
config('services.tmdb.key') &&
(settings('content.force_season_update') ||
settings('content.title_provider') === 'tmdb')
) {
$schedule
->command(UpdateSeasonsFromRemote::class)
->everyFourHours();
}
if (config('common.site.demo')) {
$schedule->command(CleanDemoSite::class)->daily();
}
$schedule->command(UpdateAllChannelsContent::class)->daily();
}
protected function commands(): void
{
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}
}