Files
memefast/database/seeders/MemeMediaSeeder.php
2025-06-13 09:44:18 +08:00

221 lines
7.4 KiB
PHP

<?php
/**
* MemeMediaSeeder - Clean Laravel Seeder
*
* Usage: php artisan db:seed --class=MemeMediaSeeder
*
* Setup:
* 1. Place webm_metadata.csv in: database/seeders/data/webm_metadata.csv
* 2. Run: php artisan db:seed --class=MemeMediaSeeder
*
* What it does:
* - Reads 223 meme records from CSV
* - 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
*/
namespace Database\Seeders;
use App\Helpers\FirstParty\MediaEngine\MediaEngine;
use App\Models\MediaCollection;
use App\Models\MemeMedia;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class MemeMediaSeeder extends Seeder
{
private const CDN_BASE_URL = 'https://cdn.memeaigen.com/g1';
private const FORMATS = [
'webm' => ['ext' => 'webm', 'mime' => 'video/webm'],
'mov' => ['ext' => 'mov', 'mime' => 'video/quicktime'],
'webp' => ['ext' => 'webp', 'mime' => 'image/webp'],
'gif' => ['ext' => 'gif', 'mime' => 'image/gif'],
];
/**
* Run the database seeds.
*/
public function run(): void
{
$this->command->info('🚀 Starting meme media import...');
// Ensure media collection exists
$this->ensureMediaCollectionExists();
// Read CSV file
$csvPath = database_path('seeders/data/webm_metadata.csv');
$memeData = $this->parseCsvFile($csvPath);
$this->command->info('📊 Found '.count($memeData).' memes to import');
// Process in chunks for memory efficiency
$chunks = array_chunk($memeData, 50);
$totalProcessed = 0;
$totalSkipped = 0;
$totalFailed = 0;
foreach ($chunks as $chunkIndex => $chunk) {
$this->command->info('Processing chunk '.($chunkIndex + 1).'/'.count($chunks));
DB::transaction(function () use ($chunk, &$totalProcessed, &$totalSkipped, &$totalFailed) {
foreach ($chunk as $memeRecord) {
try {
// Extract base filename for duplicate checking
$baseFilename = pathinfo($memeRecord['filename'], PATHINFO_FILENAME);
// Check if already exists by checking if webm_url contains this filename
if (MemeMedia::where('webm_url', 'like', "%/{$baseFilename}.webm")->exists()) {
$this->command->warn("⏭️ Skipping existing: {$memeRecord['filename']} ({$memeRecord['name']})");
$totalSkipped++;
continue;
}
$this->importSingleMeme($memeRecord);
$totalProcessed++;
if ($totalProcessed % 10 === 0) {
$this->command->info("✅ Processed {$totalProcessed} memes...");
}
} catch (\Exception $e) {
$totalFailed++;
$this->command->error("❌ Failed to import: {$memeRecord['filename']} - {$e->getMessage()}");
}
}
});
}
// Summary
$this->command->info("\n🎯 Import Summary:");
$this->command->info("✅ Successfully imported: {$totalProcessed} memes");
if ($totalSkipped > 0) {
$this->command->info("⏭️ Skipped (existing): {$totalSkipped} memes");
}
if ($totalFailed > 0) {
$this->command->error("❌ Failed: {$totalFailed} memes");
}
$totalMediaRecords = $totalProcessed * 4;
$this->command->info("📊 Created {$totalMediaRecords} media records and {$totalProcessed} meme_media records");
}
/**
* Parse CSV file and return array of meme data
*/
private function parseCsvFile(string $csvPath): array
{
if (! file_exists($csvPath)) {
throw new \RuntimeException("CSV file not found: {$csvPath}");
}
$csvContent = file_get_contents($csvPath);
$lines = str_getcsv($csvContent, "\n");
// Parse header row
$headers = str_getcsv(array_shift($lines));
$memeData = [];
foreach ($lines as $line) {
if (empty(trim($line))) {
continue;
}
$row = str_getcsv($line);
if (count($row) === count($headers)) {
$memeData[] = array_combine($headers, $row);
}
}
return $memeData;
}
/**
* Import a single meme with all its formats
*/
private function importSingleMeme(array $memeRecord): void
{
// Extract base filename (remove .webm extension)
$baseFilename = pathinfo($memeRecord['filename'], PATHINFO_FILENAME);
$mediaUuids = [];
$mediaUrls = [];
// Create MediaEngine entries for each format
foreach (self::FORMATS as $format => $config) {
$url = $this->generateCdnUrl($baseFilename, $config['ext']);
// Create media entry using save_url mode
$media = MediaEngine::addMedia(
'temps', // Media collection key
$memeRecord['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($memeRecord['name'])." ({$format})", // Name with format
null, // No specific user
$config['mime'] // MIME type
);
$mediaUuids[$format.'_uuid'] = $media->uuid;
$mediaUrls[$format.'_url'] = $url;
}
// Create MemeMedia record
MemeMedia::create([
'type' => $memeRecord['type'],
'sub_type' => $memeRecord['sub_type'],
'name' => trim($memeRecord['name']),
'description' => $memeRecord['description'],
'keywords' => $memeRecord['keywords'],
// UUIDs from MediaEngine
'mov_uuid' => $mediaUuids['mov_uuid'],
'webm_uuid' => $mediaUuids['webm_uuid'],
'gif_uuid' => $mediaUuids['gif_uuid'],
'webp_uuid' => $mediaUuids['webp_uuid'],
// Direct CDN URLs
'mov_url' => $mediaUrls['mov_url'],
'webm_url' => $mediaUrls['webm_url'],
'gif_url' => $mediaUrls['gif_url'],
'webp_url' => $mediaUrls['webp_url'],
// Embedding will be null initially
'embedding' => null,
]);
$this->command->info('✅ Imported: '.trim($memeRecord['name']));
}
/**
* Generate CDN URL for specific format
*/
private function generateCdnUrl(string $baseFilename, string $extension): string
{
return self::CDN_BASE_URL."/{$extension}/{$baseFilename}.{$extension}";
}
/**
* Ensure the temps media collection exists
*/
private function ensureMediaCollectionExists(): void
{
$collection = MediaCollection::firstOrCreate([
'key' => 'temps',
], [
'name' => 'Temp Files',
'description' => 'Temporary and external file references',
'is_system' => true,
]);
$this->command->info("📁 Using media collection: {$collection->key}");
}
}