Update
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MemeMediaSeeder - Clean Laravel Seeder
|
||||
* MemeMediaSeeder - PostgreSQL Compatible Laravel Seeder
|
||||
*
|
||||
* Usage: php artisan db:seed --class=MemeMediaSeeder
|
||||
*
|
||||
@@ -14,6 +14,7 @@
|
||||
* - Creates 4 MediaEngine entries per meme (webm, mov, webp, gif)
|
||||
* - Creates MemeMedia records with all UUIDs and URLs
|
||||
* - Uses save_url mode for fast CDN references
|
||||
* - PostgreSQL transaction-safe with individual record transactions
|
||||
*/
|
||||
|
||||
namespace Database\Seeders;
|
||||
@@ -52,41 +53,40 @@ public function run(): void
|
||||
|
||||
$this->command->info('📊 Found '.count($meme_data).' memes to import');
|
||||
|
||||
// Process in chunks for memory efficiency
|
||||
$chunks = array_chunk($meme_data, 50);
|
||||
// Process records individually for PostgreSQL compatibility
|
||||
$total_processed = 0;
|
||||
$total_skipped = 0;
|
||||
$total_failed = 0;
|
||||
|
||||
foreach ($chunks as $chunk_index => $chunk) {
|
||||
$this->command->info('Processing chunk '.($chunk_index + 1).'/'.count($chunks));
|
||||
foreach ($meme_data as $index => $meme_record) {
|
||||
$this->command->info('Processing '.($index + 1).'/'.count($meme_data).': '.$meme_record['filename']);
|
||||
|
||||
DB::transaction(function () use ($chunk, &$total_processed, &$total_skipped, &$total_failed) {
|
||||
foreach ($chunk as $meme_record) {
|
||||
try {
|
||||
// Extract base filename for duplicate checking
|
||||
$base_filename = pathinfo($meme_record['filename'], PATHINFO_FILENAME);
|
||||
try {
|
||||
// Check for duplicates OUTSIDE of transaction
|
||||
$base_filename = pathinfo($meme_record['filename'], PATHINFO_FILENAME);
|
||||
|
||||
// Check if already exists by checking if webm_url contains this filename
|
||||
if (MemeMedia::where('webm_url', 'like', "%/{$base_filename}.webm")->exists()) {
|
||||
$this->command->warn("⏭️ Skipping existing: {$meme_record['filename']} ({$meme_record['name']})");
|
||||
$total_skipped++;
|
||||
if ($this->isDuplicate($base_filename)) {
|
||||
$this->command->warn("⏭️ Skipping existing: {$meme_record['filename']} ({$meme_record['name']})");
|
||||
$total_skipped++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->importSingleMeme($meme_record);
|
||||
$total_processed++;
|
||||
|
||||
if ($total_processed % 10 === 0) {
|
||||
$this->command->info("✅ Processed {$total_processed} memes...");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$total_failed++;
|
||||
$this->command->error("❌ Failed to import: {$meme_record['filename']} - {$e->getMessage()}");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
});
|
||||
|
||||
// Process single meme in its own transaction
|
||||
$result = $this->importSingleMemeWithTransaction($meme_record);
|
||||
|
||||
if ($result) {
|
||||
$total_processed++;
|
||||
if ($total_processed % 10 === 0) {
|
||||
$this->command->info("✅ Processed {$total_processed} memes...");
|
||||
}
|
||||
} else {
|
||||
$total_failed++;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$total_failed++;
|
||||
$this->command->error("❌ Failed to import: {$meme_record['filename']} - {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
// Summary
|
||||
@@ -103,6 +103,30 @@ public function run(): void
|
||||
$this->command->info("📊 Created {$total_media_records} media records and {$total_processed} meme_media records");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if meme already exists (outside transaction)
|
||||
*/
|
||||
private function isDuplicate(string $base_filename): bool
|
||||
{
|
||||
try {
|
||||
return MemeMedia::where('webm_url', 'like', "%/{$base_filename}.webm")->exists();
|
||||
} catch (\Exception $e) {
|
||||
$this->command->warn("⚠️ Could not check duplicate for {$base_filename}: {$e->getMessage()}");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import single meme within its own transaction
|
||||
*/
|
||||
private function importSingleMemeWithTransaction(array $meme_record): bool
|
||||
{
|
||||
return DB::transaction(function () use ($meme_record) {
|
||||
return $this->importSingleMeme($meme_record);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse CSV file and return array of meme data
|
||||
*/
|
||||
@@ -136,45 +160,64 @@ private function parseCsvFile(string $csv_path): array
|
||||
/**
|
||||
* Import a single meme with all its formats
|
||||
*/
|
||||
private function importSingleMeme(array $meme_record): void
|
||||
private function importSingleMeme(array $meme_record): bool
|
||||
{
|
||||
// Extract base filename (remove .webm extension)
|
||||
$base_filename = pathinfo($meme_record['filename'], PATHINFO_FILENAME);
|
||||
try {
|
||||
// Extract base filename (remove .webm extension)
|
||||
$base_filename = pathinfo($meme_record['filename'], PATHINFO_FILENAME);
|
||||
|
||||
$media_uuids = [];
|
||||
$media_urls = [];
|
||||
$media_uuids = [];
|
||||
$media_urls = [];
|
||||
|
||||
// Create MediaEngine entries for each format
|
||||
foreach (self::FORMATS as $format => $config) {
|
||||
$url = $this->generateCdnUrl($base_filename, $config['ext']);
|
||||
// Create MediaEngine entries for each format
|
||||
foreach (self::FORMATS as $format => $config) {
|
||||
$url = $this->generateCdnUrl($base_filename, $config['ext']);
|
||||
|
||||
// Create media entry using save_url mode
|
||||
$media = MediaEngine::addMedia(
|
||||
'temps', // Media collection key
|
||||
$meme_record['type'], // video or image
|
||||
'system_uploaded', // Media source
|
||||
'meme_cdn', // Media provider
|
||||
null, // No file content
|
||||
$url, // CDN URL
|
||||
'save_url', // Mode: just save URL reference
|
||||
null, // Auto-generate filename
|
||||
'r2', // Disk (not used for URL mode)
|
||||
trim($meme_record['name'])." ({$format})", // Name with format
|
||||
null, // No specific user
|
||||
$config['mime'] // MIME type
|
||||
);
|
||||
try {
|
||||
// Create media entry using save_url mode
|
||||
$media = MediaEngine::addMedia(
|
||||
'temps', // Media collection key
|
||||
$meme_record['type'], // video or image
|
||||
'system_uploaded', // Media source
|
||||
'meme_cdn', // Media provider
|
||||
null, // No file content
|
||||
$url, // CDN URL
|
||||
'save_url', // Mode: just save URL reference
|
||||
null, // Auto-generate filename
|
||||
'r2', // Disk (not used for URL mode)
|
||||
trim($meme_record['name'])." ({$format})", // Name with format
|
||||
null, // No specific user
|
||||
$config['mime'] // MIME type
|
||||
);
|
||||
|
||||
$media_uuids[$format.'_uuid'] = $media->uuid;
|
||||
$media_urls[$format.'_url'] = $url;
|
||||
}
|
||||
$media_uuids[$format.'_uuid'] = $media->uuid;
|
||||
$media_urls[$format.'_url'] = $url;
|
||||
} catch (\Exception $e) {
|
||||
$this->command->error("Failed to create {$format} media for {$meme_record['filename']}: {$e->getMessage()}");
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$embedding = CloudflareAI::getVectorEmbeddingBgeSmall($meme_record['name'].' '.$meme_record['description'].' '.$meme_record['keywords']);
|
||||
// Generate embedding
|
||||
try {
|
||||
$embedding = CloudflareAI::getVectorEmbeddingBgeSmall(
|
||||
$meme_record['name'].' '.$meme_record['description'].' '.$meme_record['keywords']
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
$this->command->warn("Failed to generate embedding for {$meme_record['filename']}: {$e->getMessage()}");
|
||||
$embedding = null; // Continue without embedding
|
||||
}
|
||||
|
||||
// Create MemeMedia record
|
||||
// Check if record exists one more time within transaction
|
||||
$existing_meme = MemeMedia::where('original_id', $meme_record['filename'])->first();
|
||||
|
||||
$meme_media = MemeMedia::where('original_id', $meme_record['filename'])->first();
|
||||
if ($existing_meme) {
|
||||
$this->command->warn("Record already exists for {$meme_record['filename']}, skipping...");
|
||||
|
||||
if (is_null($meme_media)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create MemeMedia record
|
||||
MemeMedia::create([
|
||||
'original_id' => $meme_record['filename'],
|
||||
'type' => $meme_record['type'],
|
||||
@@ -195,12 +238,17 @@ private function importSingleMeme(array $meme_record): void
|
||||
'gif_url' => $media_urls['gif_url'],
|
||||
'webp_url' => $media_urls['webp_url'],
|
||||
|
||||
// Embedding will be null initially
|
||||
// Embedding (may be null)
|
||||
'embedding' => $embedding,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->command->info('✅ Imported: '.trim($meme_record['name']));
|
||||
$this->command->info('✅ Imported: '.trim($meme_record['name']));
|
||||
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
$this->command->error("Error importing {$meme_record['filename']}: {$e->getMessage()}");
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user