diff --git a/.gitignore b/.gitignore
index 3847c0d..8a5c823 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,7 @@ yarn-error.log
/.nova
/.vscode
/.zed
+
+# Sitemap files (auto-generated)
+/public/sitemap.xml
+/public/sitemap_*.xml
diff --git a/app/Console/Commands/GenerateMemesSitemap.php b/app/Console/Commands/GenerateMemesSitemap.php
new file mode 100644
index 0000000..418c947
--- /dev/null
+++ b/app/Console/Commands/GenerateMemesSitemap.php
@@ -0,0 +1,64 @@
+info('Starting memes sitemap generation...');
+
+ $sitemap = Sitemap::create();
+
+ // Get the base URL from config
+ $baseUrl = config('app.url');
+
+ // Get all enabled memes
+ $memes = $this->memeMediaService->getAllEnabledMemes();
+
+ $this->info("Found {$memes->count()} enabled memes");
+
+ // Add a progress bar for large datasets
+ $progressBar = $this->output->createProgressBar($memes->count());
+ $progressBar->start();
+
+ // Add each meme to the sitemap
+ foreach ($memes as $meme) {
+ $url = Url::create($baseUrl.'/meme/'.$meme->slug)
+ ->setPriority(0.8)
+ ->setChangeFrequency(Url::CHANGE_FREQUENCY_MONTHLY)
+ ->setLastModificationDate($meme->updated_at);
+
+ $sitemap->add($url);
+ $progressBar->advance();
+ }
+
+ $progressBar->finish();
+ $this->newLine();
+
+ // Save the sitemap
+ $sitemapPath = public_path('sitemap_memes.xml');
+ $sitemap->writeToFile($sitemapPath);
+
+ $this->info('Memes sitemap generated successfully!');
+ $this->info("Sitemap saved to: {$sitemapPath}");
+ $this->info("Total URLs: {$memes->count()}");
+
+ return Command::SUCCESS;
+ }
+}
diff --git a/app/Console/Commands/GenerateSitemap.php b/app/Console/Commands/GenerateSitemap.php
new file mode 100644
index 0000000..dc9dfe4
--- /dev/null
+++ b/app/Console/Commands/GenerateSitemap.php
@@ -0,0 +1,71 @@
+info('Starting main sitemap generation...');
+
+ $sitemapIndex = SitemapIndex::create();
+
+ // Get the base URL from config
+ $baseUrl = config('app.url');
+
+ // List of sub-sitemaps to include in the main sitemap
+ $subSitemaps = [
+ [
+ 'url' => $baseUrl.'/sitemap_static.xml',
+ 'lastModified' => $this->getSitemapLastModified('sitemap_static.xml'),
+ ],
+ [
+ 'url' => $baseUrl.'/sitemap_memes.xml',
+ 'lastModified' => $this->getSitemapLastModified('sitemap_memes.xml'),
+ ],
+ // Future sitemaps can be added here:
+ // [
+ // 'url' => $baseUrl . '/sitemap_pages.xml',
+ // 'lastModified' => now(),
+ // ],
+ ];
+
+ // Add each sub-sitemap to the index
+ foreach ($subSitemaps as $sitemap) {
+ $sitemapIndex->add($sitemap['url'], $sitemap['lastModified']);
+ $this->info("Added sub-sitemap: {$sitemap['url']}");
+ }
+
+ // Save the main sitemap index
+ $sitemapPath = public_path('sitemap.xml');
+ $sitemapIndex->writeToFile($sitemapPath);
+
+ $this->info('Main sitemap index generated successfully!');
+ $this->info("Sitemap saved to: {$sitemapPath}");
+ $this->info('Total sub-sitemaps: '.count($subSitemaps));
+
+ return Command::SUCCESS;
+ }
+
+ /**
+ * Get the last modification date of a sitemap file
+ */
+ private function getSitemapLastModified(string $filename): \DateTime
+ {
+ $filepath = public_path($filename);
+
+ if (file_exists($filepath)) {
+ return new \DateTime('@'.filemtime($filepath));
+ }
+
+ // If file doesn't exist, return current time
+ return new \DateTime;
+ }
+}
diff --git a/app/Console/Commands/GenerateStaticSitemap.php b/app/Console/Commands/GenerateStaticSitemap.php
new file mode 100644
index 0000000..9f2fe39
--- /dev/null
+++ b/app/Console/Commands/GenerateStaticSitemap.php
@@ -0,0 +1,80 @@
+info('Starting static sitemap generation...');
+
+ $sitemap = Sitemap::create();
+
+ // Get the base URL from config
+ $baseUrl = config('app.url');
+
+ // Add static pages
+ $staticPages = [
+ [
+ 'url' => $baseUrl,
+ 'priority' => 1.0,
+ 'changeFrequency' => Url::CHANGE_FREQUENCY_WEEKLY,
+ 'lastModificationDate' => now(),
+ ],
+ [
+ 'url' => $baseUrl.'/meme-library',
+ 'priority' => 0.9,
+ 'changeFrequency' => Url::CHANGE_FREQUENCY_DAILY,
+ 'lastModificationDate' => now(),
+ ],
+ [
+ 'url' => $baseUrl.'/privacy',
+ 'priority' => 0.3,
+ 'changeFrequency' => Url::CHANGE_FREQUENCY_YEARLY,
+ 'lastModificationDate' => now()->subMonths(6), // Adjust based on when you last updated
+ ],
+ [
+ 'url' => $baseUrl.'/terms',
+ 'priority' => 0.3,
+ 'changeFrequency' => Url::CHANGE_FREQUENCY_YEARLY,
+ 'lastModificationDate' => now()->subMonths(6), // Adjust based on when you last updated
+ ],
+ [
+ 'url' => $baseUrl.'/dmca-copyright',
+ 'priority' => 0.2,
+ 'changeFrequency' => Url::CHANGE_FREQUENCY_YEARLY,
+ 'lastModificationDate' => now()->subMonths(6), // Adjust based on when you last updated
+ ],
+ ];
+
+ // Add each static page to the sitemap
+ foreach ($staticPages as $page) {
+ $url = Url::create($page['url'])
+ ->setPriority($page['priority'])
+ ->setChangeFrequency($page['changeFrequency'])
+ ->setLastModificationDate($page['lastModificationDate']);
+
+ $sitemap->add($url);
+
+ $this->info("Added: {$page['url']}");
+ }
+
+ // Save the sitemap
+ $sitemapPath = public_path('sitemap_static.xml');
+ $sitemap->writeToFile($sitemapPath);
+
+ $this->info('Static sitemap generated successfully!');
+ $this->info("Sitemap saved to: {$sitemapPath}");
+ $this->info('Total URLs: '.count($staticPages));
+
+ return Command::SUCCESS;
+ }
+}
diff --git a/app/Console/Commands/PopulateMemeMediaSlugs.php b/app/Console/Commands/PopulateMemeMediaSlugs.php
new file mode 100644
index 0000000..9bd9d54
--- /dev/null
+++ b/app/Console/Commands/PopulateMemeMediaSlugs.php
@@ -0,0 +1,51 @@
+info('Starting to populate MemeMedia slugs...');
+
+ $memes = MemeMedia::whereNull('slug')->get();
+
+ if ($memes->isEmpty()) {
+ $this->info('No memes found without slugs.');
+
+ return;
+ }
+
+ $this->info("Found {$memes->count()} memes without slugs.");
+
+ $bar = $this->output->createProgressBar($memes->count());
+ $bar->start();
+
+ foreach ($memes as $meme) {
+ $baseSlug = Str::slug($meme->name);
+ $slug = $baseSlug;
+ $counter = 1;
+
+ // Ensure slug is unique
+ while (MemeMedia::where('slug', $slug)->exists()) {
+ $slug = $baseSlug.'-'.$counter;
+ $counter++;
+ }
+
+ $meme->update(['slug' => $slug]);
+ $bar->advance();
+ }
+
+ $bar->finish();
+ $this->newLine();
+ $this->info('Successfully populated all MemeMedia slugs!');
+ }
+}
diff --git a/app/Http/Controllers/FrontHomeController.php b/app/Http/Controllers/FrontHomeController.php
index e23060f..b51597f 100644
--- a/app/Http/Controllers/FrontHomeController.php
+++ b/app/Http/Controllers/FrontHomeController.php
@@ -4,6 +4,7 @@
use App\Models\BackgroundMedia;
use App\Models\MemeMedia;
+use App\Services\MemeMediaService;
use Artesaos\SEOTools\Facades\JsonLd;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
@@ -11,6 +12,10 @@
class FrontHomeController extends Controller
{
+ public function __construct(
+ private MemeMediaService $memeMediaService
+ ) {}
+
public function index()
{
if (App::environment('production') && env('COMING_SOON_ENABLED', true)) {
@@ -28,12 +33,16 @@ public function index()
// Get FAQ data
$faqData = $this->getFaqData();
+ // Get popular keywords for search suggestions
+ $popularKeywords = $this->memeMediaService->getPopularKeywords(10);
+
// Add FAQ JSON-LD structured data
$this->addFaqJsonLd($faqData);
return Inertia::render('home/home', [
'stats' => $stats,
'faqData' => $faqData,
+ 'popularKeywords' => $popularKeywords,
]);
}
diff --git a/app/Http/Controllers/FrontMemeController.php b/app/Http/Controllers/FrontMemeController.php
new file mode 100644
index 0000000..d21b805
--- /dev/null
+++ b/app/Http/Controllers/FrontMemeController.php
@@ -0,0 +1,333 @@
+getMemes($request->input('search'));
+ }
+
+ public function search(string $search): Response
+ {
+ // Convert + back to spaces for search
+ $searchTerm = str_replace('+', ' ', $search);
+
+ return $this->getMemes($searchTerm);
+ }
+
+ private function getMemes(?string $search = null): Response
+ {
+ $memes = $this->memeMediaService->searchMemes($search, 24);
+
+ // Set up SEO meta tags
+ $title = $search ? ucfirst($search).' Memes in MEMEFA.ST' : 'Meme Library - Thousands of Video Meme Templates';
+
+ if ($search) {
+ // Get SEO descriptions from config
+ $descriptions = config('platform.seo_descriptions.search_descriptions', []);
+
+ // Use deterministic selection based on search term hash
+ $searchHash = crc32($search);
+ $descriptionIndex = abs($searchHash) % count($descriptions);
+ $descriptionTemplate = $descriptions[$descriptionIndex];
+
+ // Replace keyword placeholder
+ $description = str_replace('__KEYWORD__', $search, $descriptionTemplate);
+ } else {
+ $description = 'Browse thousands of video meme templates ready for TikTok, Instagram Reels, Threads and YouTube Shorts. Create viral content in minutes with our meme editor.';
+ }
+
+ SEOMeta::setTitle($title, false);
+ SEOMeta::setDescription($description);
+ SEOMeta::setCanonical(request()->url());
+
+ // Add pagination rel links
+ if ($memes->previousPageUrl()) {
+ SEOMeta::addMeta('link:prev', $memes->previousPageUrl(), 'rel');
+ }
+ if ($memes->nextPageUrl()) {
+ SEOMeta::addMeta('link:next', $memes->nextPageUrl(), 'rel');
+ }
+
+ // OpenGraph tags
+ OpenGraph::setTitle($title);
+ OpenGraph::setDescription($description);
+ OpenGraph::setUrl(request()->url());
+ OpenGraph::addProperty('type', 'website');
+
+ // Twitter Card
+ TwitterCard::setTitle($title);
+ TwitterCard::setDescription($description);
+ TwitterCard::setType('summary_large_image');
+
+ // Get available types for filter
+ $types = $this->memeMediaService->getAvailableTypes();
+
+ // Get popular keywords for filter
+ $popularKeywords = $this->memeMediaService->getPopularKeywords(20);
+
+ return Inertia::render('memes/index', [
+ 'memes' => $memes,
+ 'types' => $types,
+ 'popularKeywords' => $popularKeywords,
+ 'filters' => [
+ 'search' => $search,
+ ],
+ 'dynamicDescription' => $search ? $description : null,
+ ]);
+ }
+
+ public function show(string $slug): Response
+ {
+ $meme = $this->memeMediaService->findBySlug($slug);
+
+ // Get related memes based on similar keywords
+ $relatedMemes = $this->memeMediaService->getRelatedMemes($meme, 6);
+
+ // Set up SEO meta tags for individual meme page
+ $title = "{$meme->name} - Make Video Memes with MEMEFA.ST";
+ $description = $meme->description
+ ? "This meme is about: {$meme->description}."
+ : "Create {$meme->name} video memes with our online editor. Perfect for TikTok, Instagram Reels, and YouTube Shorts.";
+
+ SEOMeta::setTitle($title, false);
+ SEOMeta::setDescription($description);
+ SEOMeta::setCanonical(request()->url());
+ SEOMeta::addKeyword(collect([$meme->keywords, $meme->action_keywords, $meme->emotion_keywords, $meme->misc_keywords])->flatten()->filter()->implode(', '));
+
+ // OpenGraph tags
+ OpenGraph::setTitle($title);
+ OpenGraph::setDescription($description);
+ OpenGraph::setUrl(request()->url());
+ OpenGraph::addProperty('type', 'video.other');
+ OpenGraph::addImage(route('memes.og', $meme->ids));
+
+ // Twitter Card
+ TwitterCard::setTitle($title);
+ TwitterCard::setDescription($description);
+ TwitterCard::setType('summary_large_image');
+ TwitterCard::setImage(route('memes.og', $meme->ids));
+
+ return Inertia::render('memes/show', [
+ 'meme' => $meme,
+ 'relatedMemes' => $relatedMemes,
+ ]);
+ }
+
+ public function generateOG(string $ids): HttpResponse
+ {
+ // Get the meme media using the service
+ $meme = $this->memeMediaService->findByHashIds($ids);
+
+ // Load the template image
+ $templatePath = public_path('memefast-og-template.jpg');
+
+ if (! file_exists($templatePath)) {
+ abort(404, 'Template image not found');
+ }
+
+ // Create ImageManager with Imagick driver
+ $manager = new ImageManager(new Driver);
+
+ // Load the template image
+ $image = $manager->read($templatePath);
+
+ // Ensure RGB colorspace for proper color rendering
+ $imagick = $image->core()->native();
+ $imagick->setColorspace(\Imagick::COLORSPACE_SRGB);
+
+ // Load the meme image from URL
+ if ($meme->webp_url) {
+ try {
+ // Use cURL to get the image content with proper headers
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $meme->webp_url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MemeFast OG Generator)');
+
+ $imageContent = curl_exec($ch);
+ $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ curl_close($ch);
+
+ if ($imageContent !== false && $httpCode === 200) {
+ $memeImage = $manager->read($imageContent);
+
+ // Image positioning variables
+ $imageX = 0; // Horizontal offset
+ $imageY = 100; // Vertical offset
+
+ // Resize meme image to 1.5x the template height while maintaining aspect ratio
+ $memeImage = $memeImage->scale(height: 1350);
+
+ // Place the meme image vertically centered, horizontally right-justified
+ $image->place($memeImage, 'center-right', $imageX, $imageY);
+ } else {
+ $image->text('HTTP Error: '.$httpCode, 200, 200, function ($font) {
+ $font->size(20);
+ $font->color('#ff0000');
+ $font->align('center');
+ });
+ }
+ } catch (\Exception $e) {
+ // If meme image fails to load, add debug text
+ $image->text('Image failed to load: '.$e->getMessage(), 200, 200, function ($font) {
+ $font->size(20);
+ $font->color('#ff0000');
+ $font->align('center');
+ });
+ }
+ }
+
+ // Add the meme name as text with proper kerning and line wrapping
+ if ($meme->name) {
+ $fontPath = resource_path('fonts/Geist/Geist-Bold.ttf');
+
+ // Fallback to built-in font if TTF not found
+ if (file_exists($fontPath)) {
+ // Use native Imagick for text kerning with line wrapping
+ $imagick = $image->core()->native(); // Get the native Imagick object
+
+ // First draw the green outline
+ $drawOutline = new \ImagickDraw;
+ $drawOutline->setFillColor('#00FF00');
+ $drawOutline->setFont($fontPath);
+ $drawOutline->setFontSize(70);
+ $drawOutline->setTextAlignment(\Imagick::ALIGN_LEFT);
+ $drawOutline->setStrokeColor('#00FF00');
+ $drawOutline->setStrokeWidth(20); // Thicker stroke for outline effect
+
+ // Then draw the black text on top
+ $draw = new \ImagickDraw;
+ $draw->setFillColor('#000000');
+ $draw->setFont($fontPath);
+ $draw->setFontSize(70);
+ // $draw->setTextKerning(-4); // Negative for tighter kerning
+ $draw->setTextAlignment(\Imagick::ALIGN_LEFT);
+
+ // Text wrapping - 70% of canvas width (1600 * 0.7 = 1120px)
+ $maxWidth = 1120;
+ $text = 'Make meme videos instantly with '.$meme->name.' meme with';
+
+ // Split text into words
+ $words = explode(' ', $text);
+ $lines = [];
+ $currentLine = '';
+
+ foreach ($words as $word) {
+ $testLine = $currentLine.($currentLine ? ' ' : '').$word;
+
+ // Get text metrics for the test line
+ $metrics = $imagick->queryFontMetrics($draw, $testLine);
+
+ if ($metrics['textWidth'] > $maxWidth && $currentLine !== '') {
+ // Line is too long, save current line and start new one
+ $lines[] = $currentLine;
+ $currentLine = $word;
+ } else {
+ $currentLine = $testLine;
+ }
+ }
+
+ // Add the last line
+ if ($currentLine) {
+ $lines[] = $currentLine;
+ }
+
+ $lines[] .= 'MEMEFAST';
+
+ // Text positioning variables
+ $textX = 100; // Horizontal position
+ $textY = 320; // Base vertical position
+
+ // Calculate line height and starting Y position
+ $lineHeight = 75; // Tighter line spacing
+ $totalHeight = count($lines) * $lineHeight;
+ $startY = $textY - ($totalHeight / 2); // Center vertically
+
+ // Draw each line (left-aligned) - first the outline, then the text
+ foreach ($lines as $index => $line) {
+ $y = $startY + ($index * $lineHeight);
+
+ // Check if this is the MEMEFA.ST line
+ if ($line === 'MEMEFAST') {
+
+ $extraSizing = 40;
+
+ // Use Bungee font for MEMEFA.ST
+ $bungeeFontPath = resource_path('fonts/Bungee/Bungee-Regular.ttf');
+
+ if (file_exists($bungeeFontPath)) {
+ // Create separate draw objects for Bungee font
+ $bungeeOutline = new \ImagickDraw;
+ $bungeeOutline->setFillColor('#00FF00');
+ $bungeeOutline->setFont($bungeeFontPath);
+ $bungeeOutline->setFontSize(70);
+ $bungeeOutline->setTextAlignment(\Imagick::ALIGN_LEFT);
+ $bungeeOutline->setStrokeColor('#00FF00');
+ $bungeeOutline->setStrokeWidth(20);
+
+ $bungeeText = new \ImagickDraw;
+ $bungeeText->setFillColor('#000000');
+ $bungeeText->setFont($bungeeFontPath);
+ $bungeeText->setFontSize(70 + $extraSizing);
+ $bungeeText->setTextAlignment(\Imagick::ALIGN_LEFT);
+
+ // Draw with Bungee font
+ $bungeeOutline->annotation($textX, $y + $extraSizing, $line);
+ $bungeeText->annotation($textX, $y + $extraSizing, $line);
+
+ $imagick->drawImage($bungeeOutline);
+ $imagick->drawImage($bungeeText);
+ } else {
+ // Fallback to regular font
+ $drawOutline->annotation($textX, $y + $extraSizing, $line);
+ $draw->annotation($textX, $y + $extraSizing, $line);
+ }
+ } else {
+ // Use regular font for other lines
+ $drawOutline->annotation($textX, $y, $line);
+ $draw->annotation($textX, $y, $line);
+ }
+ }
+
+ // Apply the drawing to the image (only for non-Bungee lines)
+ $imagick->drawImage($drawOutline); // Draw outline first
+ $imagick->drawImage($draw); // Draw text on top
+ } else {
+ $image->text($meme->name, 400, 450, function ($font) {
+ $font->size(80);
+ $font->color('#000000');
+ $font->align('center');
+ $font->valign('middle');
+ });
+ }
+ }
+
+ // Generate the image and return as response
+ $encodedImage = $image->toJpeg(100);
+
+ return response($encodedImage, 200, [
+ 'Content-Type' => 'image/jpeg',
+ 'Cache-Control' => 'public, max-age=3600',
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/FrontPagesController.php b/app/Http/Controllers/FrontPagesController.php
index 83f8bfa..dabd83c 100644
--- a/app/Http/Controllers/FrontPagesController.php
+++ b/app/Http/Controllers/FrontPagesController.php
@@ -2,6 +2,9 @@
namespace App\Http\Controllers;
+use Artesaos\SEOTools\Facades\OpenGraph;
+use Artesaos\SEOTools\Facades\SEOMeta;
+use Artesaos\SEOTools\Facades\TwitterCard;
use Illuminate\Support\Str;
use Inertia\Inertia;
@@ -9,6 +12,25 @@ class FrontPagesController extends Controller
{
public function privacy()
{
+ // Set up SEO meta tags
+ $title = 'Privacy Policy';
+ $description = 'Read our privacy policy to understand how Memefast collects, uses, and protects your personal information when using our video meme creation platform.';
+
+ SEOMeta::setTitle($title);
+ SEOMeta::setDescription($description);
+ SEOMeta::setCanonical(request()->url());
+
+ // OpenGraph tags
+ OpenGraph::setTitle($title);
+ OpenGraph::setDescription($description);
+ OpenGraph::setUrl(request()->url());
+ OpenGraph::addProperty('type', 'website');
+
+ // Twitter Card
+ TwitterCard::setTitle($title);
+ TwitterCard::setDescription($description);
+ TwitterCard::setType('summary');
+
$markdownPath = resource_path('markdown/privacy.md');
$markdownContent = file_get_contents($markdownPath);
@@ -26,6 +48,25 @@ public function privacy()
public function terms()
{
+ // Set up SEO meta tags
+ $title = 'Terms & Conditions';
+ $description = 'Review our terms and conditions to understand the rules and guidelines for using Memefast video meme creation platform and services.';
+
+ SEOMeta::setTitle($title);
+ SEOMeta::setDescription($description);
+ SEOMeta::setCanonical(request()->url());
+
+ // OpenGraph tags
+ OpenGraph::setTitle($title);
+ OpenGraph::setDescription($description);
+ OpenGraph::setUrl(request()->url());
+ OpenGraph::addProperty('type', 'website');
+
+ // Twitter Card
+ TwitterCard::setTitle($title);
+ TwitterCard::setDescription($description);
+ TwitterCard::setType('summary');
+
$markdownPath = resource_path('markdown/terms.md');
$markdownContent = file_get_contents($markdownPath);
@@ -41,6 +82,42 @@ public function terms()
]);
}
+ public function dmcaCopyright()
+ {
+ // Set up SEO meta tags
+ $title = 'DMCA Copyright Policy';
+ $description = 'MEMEFA.ST DMCA copyright policy and procedures for reporting copyright infringement. Learn how to file DMCA notices and counter-notices.';
+
+ SEOMeta::setTitle($title);
+ SEOMeta::setDescription($description);
+ SEOMeta::setCanonical(request()->url());
+
+ // OpenGraph tags
+ OpenGraph::setTitle($title);
+ OpenGraph::setDescription($description);
+ OpenGraph::setUrl(request()->url());
+ OpenGraph::addProperty('type', 'website');
+
+ // Twitter Card
+ TwitterCard::setTitle($title);
+ TwitterCard::setDescription($description);
+ TwitterCard::setType('summary');
+
+ $markdownPath = resource_path('markdown/dmca-copyright.md');
+ $markdownContent = file_get_contents($markdownPath);
+
+ // Parse markdown to HTML using Laravel's built-in Str::markdown helper
+ $htmlContent = Str::markdown($markdownContent);
+
+ // Style the HTML with Tailwind classes
+ $styledContent = $this->styleHtmlContent($htmlContent);
+
+ return Inertia::render('FrontPages/DmcaCopyright', [
+ 'content' => $styledContent,
+ 'title' => 'DMCA Copyright Policy',
+ ]);
+ }
+
private function styleHtmlContent($html)
{
// Add classes to various HTML elements using string replacement for Tailwind 4
diff --git a/app/Models/MemeMedia.php b/app/Models/MemeMedia.php
index 66f599e..653e46c 100644
--- a/app/Models/MemeMedia.php
+++ b/app/Models/MemeMedia.php
@@ -53,6 +53,7 @@ class MemeMedia extends Model
'sub_type',
'original_id',
'name',
+ 'slug',
'description',
'keywords',
'group',
@@ -79,7 +80,7 @@ class MemeMedia extends Model
'type',
'sub_type',
'original_id',
- 'description',
+ // 'description',
'mov_uuid',
'webm_uuid',
'gif_uuid',
diff --git a/app/Services/MemeMediaService.php b/app/Services/MemeMediaService.php
new file mode 100644
index 0000000..c96f6e1
--- /dev/null
+++ b/app/Services/MemeMediaService.php
@@ -0,0 +1,172 @@
+buildSearchQuery($search);
+
+ return $query->cursorPaginate($perPage);
+ }
+
+ /**
+ * Get popular keywords for filtering
+ */
+ public function getPopularKeywords(int $limit = 20): SupportCollection
+ {
+ $cacheKey = "popular_keywords_limit_{$limit}";
+
+ return Cache::remember($cacheKey, 60 * 60 * 24, function () use ($limit) {
+ return MemeMedia::where('is_enabled', true)
+ ->get()
+ ->pluck('keywords')
+ ->flatten()
+ ->countBy()
+ ->sort()
+ ->reverse()
+ ->take($limit)
+ ->keys();
+ });
+ }
+
+ /**
+ * Get available meme types for filtering
+ */
+ public function getAvailableTypes(): SupportCollection
+ {
+ return MemeMedia::where('is_enabled', true)
+ ->distinct()
+ ->pluck('type')
+ ->filter();
+ }
+
+ /**
+ * Find meme by slug
+ */
+ public function findBySlug(string $slug): MemeMedia
+ {
+ return MemeMedia::where('slug', $slug)
+ ->where('is_enabled', true)
+ ->firstOrFail();
+ }
+
+ /**
+ * Find meme by hashids
+ */
+ public function findByHashIds(string $ids): MemeMedia
+ {
+ $memeId = hashids_decode($ids);
+
+ if (! $memeId) {
+ throw new ModelNotFoundException('Meme not found');
+ }
+
+ return MemeMedia::where('id', $memeId)
+ ->where('is_enabled', true)
+ ->firstOrFail();
+ }
+
+ /**
+ * Get related memes based on keywords
+ */
+ public function getRelatedMemes(MemeMedia $meme, int $limit = 6): Collection
+ {
+ $relatedMemes = MemeMedia::where('is_enabled', true)
+ ->where('id', '!=', $meme->id)
+ ->where(function ($query) use ($meme) {
+ if ($meme->keywords) {
+ foreach ($meme->keywords as $keyword) {
+ $query->orWhereJsonContains('keywords', $keyword)
+ ->orWhereJsonContains('action_keywords', $keyword)
+ ->orWhereJsonContains('emotion_keywords', $keyword)
+ ->orWhereJsonContains('misc_keywords', $keyword);
+ }
+ }
+ })
+ ->limit($limit)
+ ->get();
+
+ // If we have less than the desired limit, fill up with random ones
+ if ($relatedMemes->count() < $limit) {
+ $excludeIds = $relatedMemes->pluck('id')->push($meme->id)->toArray();
+ $needed = $limit - $relatedMemes->count();
+
+ $randomMemes = $this->fillWithRandomMemes($relatedMemes, $limit, $excludeIds);
+ $relatedMemes = $relatedMemes->merge($randomMemes);
+ }
+
+ return $relatedMemes;
+ }
+
+ /**
+ * Fill collection with random memes up to target count
+ */
+ public function fillWithRandomMemes(Collection $existing, int $targetCount, array $excludeIds): Collection
+ {
+ $needed = $targetCount - $existing->count();
+
+ if ($needed <= 0) {
+ return collect();
+ }
+
+ return MemeMedia::where('is_enabled', true)
+ ->whereNotIn('id', $excludeIds)
+ ->inRandomOrder()
+ ->limit($needed)
+ ->get();
+ }
+
+ /**
+ * Build search query with keyword matching
+ */
+ private function buildSearchQuery(?string $search = null): Builder
+ {
+ $query = $this->getEnabledMemesQuery();
+
+ if ($search) {
+ $query->where(function ($q) use ($search) {
+ $q->where('name', 'ilike', "%{$search}%")
+ ->orWhere('description', 'ilike', "%{$search}%")
+ ->orWhereJsonContains('keywords', $search)
+ ->orWhereJsonContains('action_keywords', $search)
+ ->orWhereJsonContains('emotion_keywords', $search)
+ ->orWhereJsonContains('misc_keywords', $search);
+ });
+ }
+
+ return $query;
+ }
+
+ /**
+ * Get all enabled memes for sitemap generation
+ */
+ public function getAllEnabledMemes(): Collection
+ {
+ return MemeMedia::where('is_enabled', true)
+ ->orderBy('updated_at', 'desc')
+ ->get();
+ }
+
+ /**
+ * Get base query for enabled memes
+ */
+ private function getEnabledMemesQuery(): Builder
+ {
+ return MemeMedia::query()
+ ->where('is_enabled', true)
+ ->orderBy('id', 'desc');
+ }
+}
diff --git a/composer.json b/composer.json
index 5247952..5674c40 100644
--- a/composer.json
+++ b/composer.json
@@ -12,6 +12,7 @@
"php": "^8.2",
"artesaos/seotools": "^1.3",
"inertiajs/inertia-laravel": "^2.0",
+ "intervention/image": "^3.11",
"jenssegers/imagehash": "^0.10.0",
"kalnoy/nestedset": "^6.0",
"laravel/cashier": "^15.7",
@@ -25,6 +26,7 @@
"pbmedia/laravel-ffmpeg": "^8.7",
"pgvector/pgvector": "^0.2.2",
"spatie/laravel-responsecache": "^7.7",
+ "spatie/laravel-sitemap": "^7.3",
"spatie/laravel-tags": "^4.10",
"symfony/dom-crawler": "^7.3",
"tightenco/ziggy": "^2.4",
diff --git a/composer.lock b/composer.lock
index 4336334..ba2ec69 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "dbe9b4012c67d84fc322f58ddb9af791",
+ "content-hash": "79a02fbcd504e47b61331dad19bd62a0",
"packages": [
{
"name": "artesaos/seotools",
@@ -4327,6 +4327,60 @@
},
"time": "2025-03-30T21:06:30+00:00"
},
+ {
+ "name": "nicmart/tree",
+ "version": "0.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nicmart/Tree.git",
+ "reference": "f5e17bf18d78cfb0666ebb9f956c3acd8d14229d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nicmart/Tree/zipball/f5e17bf18d78cfb0666ebb9f956c3acd8d14229d",
+ "reference": "f5e17bf18d78cfb0666ebb9f956c3acd8d14229d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
+ },
+ "require-dev": {
+ "ergebnis/composer-normalize": "^2.44.0",
+ "ergebnis/license": "^2.6.0",
+ "ergebnis/php-cs-fixer-config": "^6.28.1",
+ "fakerphp/faker": "^1.24.1",
+ "infection/infection": "~0.26.19",
+ "phpunit/phpunit": "^9.6.19",
+ "psalm/plugin-phpunit": "~0.19.0",
+ "vimeo/psalm": "^5.26.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Tree\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolò Martini",
+ "email": "nicmartnic@gmail.com"
+ },
+ {
+ "name": "Andreas Möller",
+ "email": "am@localheinz.com"
+ }
+ ],
+ "description": "A basic but flexible php tree data structure and a fluent tree builder implementation.",
+ "support": {
+ "issues": "https://github.com/nicmart/Tree/issues",
+ "source": "https://github.com/nicmart/Tree/tree/0.9.0"
+ },
+ "time": "2024-11-22T15:36:01+00:00"
+ },
{
"name": "nikic/php-parser",
"version": "v5.4.0",
@@ -5857,6 +5911,142 @@
],
"time": "2024-04-27T21:32:50+00:00"
},
+ {
+ "name": "spatie/browsershot",
+ "version": "5.0.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/browsershot.git",
+ "reference": "9e5ae15487b3cdc3eb03318c1c8ac38971f60e58"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/browsershot/zipball/9e5ae15487b3cdc3eb03318c1c8ac38971f60e58",
+ "reference": "9e5ae15487b3cdc3eb03318c1c8ac38971f60e58",
+ "shasum": ""
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "ext-json": "*",
+ "php": "^8.2",
+ "spatie/temporary-directory": "^2.0",
+ "symfony/process": "^6.0|^7.0"
+ },
+ "require-dev": {
+ "pestphp/pest": "^3.0",
+ "spatie/image": "^3.6",
+ "spatie/pdf-to-text": "^1.52",
+ "spatie/phpunit-snapshot-assertions": "^4.2.3|^5.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Spatie\\Browsershot\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://github.com/freekmurze",
+ "role": "Developer"
+ }
+ ],
+ "description": "Convert a webpage to an image or pdf using headless Chrome",
+ "homepage": "https://github.com/spatie/browsershot",
+ "keywords": [
+ "chrome",
+ "convert",
+ "headless",
+ "image",
+ "pdf",
+ "puppeteer",
+ "screenshot",
+ "webpage"
+ ],
+ "support": {
+ "source": "https://github.com/spatie/browsershot/tree/5.0.10"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/spatie",
+ "type": "github"
+ }
+ ],
+ "time": "2025-05-15T07:10:57+00:00"
+ },
+ {
+ "name": "spatie/crawler",
+ "version": "8.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/crawler.git",
+ "reference": "4f4c3ead439e7e57085c0b802bc4e5b44fb7d751"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/crawler/zipball/4f4c3ead439e7e57085c0b802bc4e5b44fb7d751",
+ "reference": "4f4c3ead439e7e57085c0b802bc4e5b44fb7d751",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/guzzle": "^7.3",
+ "guzzlehttp/psr7": "^2.0",
+ "illuminate/collections": "^10.0|^11.0|^12.0",
+ "nicmart/tree": "^0.9",
+ "php": "^8.2",
+ "spatie/browsershot": "^5.0.5",
+ "spatie/robots-txt": "^2.0",
+ "symfony/dom-crawler": "^6.0|^7.0"
+ },
+ "require-dev": {
+ "pestphp/pest": "^2.0|^3.0",
+ "spatie/ray": "^1.37"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Spatie\\Crawler\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be"
+ }
+ ],
+ "description": "Crawl all internal links found on a website",
+ "homepage": "https://github.com/spatie/crawler",
+ "keywords": [
+ "crawler",
+ "link",
+ "spatie",
+ "website"
+ ],
+ "support": {
+ "issues": "https://github.com/spatie/crawler/issues",
+ "source": "https://github.com/spatie/crawler/tree/8.4.3"
+ },
+ "funding": [
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/spatie",
+ "type": "github"
+ }
+ ],
+ "time": "2025-05-20T09:00:51+00:00"
+ },
{
"name": "spatie/eloquent-sortable",
"version": "4.5.0",
@@ -6075,6 +6265,79 @@
],
"time": "2025-05-20T08:39:19+00:00"
},
+ {
+ "name": "spatie/laravel-sitemap",
+ "version": "7.3.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/laravel-sitemap.git",
+ "reference": "506b2acdd350c7ff868a7711b4f30e486b20e9b0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/laravel-sitemap/zipball/506b2acdd350c7ff868a7711b4f30e486b20e9b0",
+ "reference": "506b2acdd350c7ff868a7711b4f30e486b20e9b0",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/guzzle": "^7.8",
+ "illuminate/support": "^11.0|^12.0",
+ "nesbot/carbon": "^2.71|^3.0",
+ "php": "^8.2||^8.3||^8.4",
+ "spatie/crawler": "^8.0.1",
+ "spatie/laravel-package-tools": "^1.16.1",
+ "symfony/dom-crawler": "^6.3.4|^7.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.6.6",
+ "orchestra/testbench": "^9.0|^10.0",
+ "pestphp/pest": "^3.7.4",
+ "spatie/pest-plugin-snapshots": "^2.1",
+ "spatie/phpunit-snapshot-assertions": "^5.1.2",
+ "spatie/temporary-directory": "^2.2"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Spatie\\Sitemap\\SitemapServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Spatie\\Sitemap\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
+ }
+ ],
+ "description": "Create and generate sitemaps with ease",
+ "homepage": "https://github.com/spatie/laravel-sitemap",
+ "keywords": [
+ "laravel-sitemap",
+ "spatie"
+ ],
+ "support": {
+ "source": "https://github.com/spatie/laravel-sitemap/tree/7.3.6"
+ },
+ "funding": [
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ }
+ ],
+ "time": "2025-04-10T12:13:41+00:00"
+ },
{
"name": "spatie/laravel-tags",
"version": "4.10.0",
@@ -6228,6 +6491,66 @@
],
"time": "2025-02-20T15:51:22+00:00"
},
+ {
+ "name": "spatie/robots-txt",
+ "version": "2.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/robots-txt.git",
+ "reference": "ef85dfaa48372c0a7fdfb144592f95de1a2e9b79"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/robots-txt/zipball/ef85dfaa48372c0a7fdfb144592f95de1a2e9b79",
+ "reference": "ef85dfaa48372c0a7fdfb144592f95de1a2e9b79",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Spatie\\Robots\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Brent Roose",
+ "email": "brent@spatie.be",
+ "homepage": "https://spatie.be",
+ "role": "Developer"
+ }
+ ],
+ "description": "Determine if a page may be crawled from robots.txt and robots meta tags",
+ "homepage": "https://github.com/spatie/robots-txt",
+ "keywords": [
+ "robots-txt",
+ "spatie"
+ ],
+ "support": {
+ "issues": "https://github.com/spatie/robots-txt/issues",
+ "source": "https://github.com/spatie/robots-txt/tree/2.5.1"
+ },
+ "funding": [
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/spatie",
+ "type": "github"
+ }
+ ],
+ "time": "2025-07-01T07:07:44+00:00"
+ },
{
"name": "spatie/temporary-directory",
"version": "2.3.0",
diff --git a/config/platform/seo_descriptions.php b/config/platform/seo_descriptions.php
new file mode 100644
index 0000000..a880493
--- /dev/null
+++ b/config/platform/seo_descriptions.php
@@ -0,0 +1,116 @@
+ [
+ 'Create hilarious __KEYWORD__ memes with our video editor. Browse __KEYWORD__ memes perfect for sharing on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make funny __KEYWORD__ memes fast. Share with friends on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit sharp __KEYWORD__ memes in minutes. Send to friends on WhatsApp, Telegram, Discord. Works great for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make clean __KEYWORD__ memes that slay. Share on WhatsApp, Telegram, Discord. Designed for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Turn clips into solid __KEYWORD__ memes. Send to group chats on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create scroll-stopping __KEYWORD__ memes. Share with friends on WhatsApp, Telegram, Discord. Optimized for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make short, punchy __KEYWORD__ memes. Send to your group on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit quick __KEYWORD__ memes that hit. Share on WhatsApp, Telegram, Discord. Best for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Drop clean __KEYWORD__ memes online. Send to friends on WhatsApp, Telegram, Discord. Works across TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Build smooth __KEYWORD__ memes with ease. Share on WhatsApp, Telegram, Discord. Ready to post on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Craft tight __KEYWORD__ memes in your browser. Send to group chats on WhatsApp, Telegram, Discord. Made for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Design fresh __KEYWORD__ memes fast. Share with friends on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create polished __KEYWORD__ memes. Send to your crew on WhatsApp, Telegram, Discord. Ideal for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit clean-cut __KEYWORD__ memes with no hassle. Share on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make crisp __KEYWORD__ memes that just work. Send to friends on WhatsApp, Telegram, Discord. Works well on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Build meme-ready __KEYWORD__ clips. Share on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Craft easy __KEYWORD__ memes for short-form. Send to group chats on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Generate funny __KEYWORD__ memes fast. Share with friends on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Drop solid __KEYWORD__ memes from your clips. Send to your squad on WhatsApp, Telegram, Discord. Optimized for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make short-form __KEYWORD__ memes that pop. Share on WhatsApp, Telegram, Discord. Built for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit no-fuss __KEYWORD__ memes that land. Send to friends on WhatsApp, Telegram, Discord. Works on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create well-paced __KEYWORD__ memes fast. Share on WhatsApp, Telegram, Discord. Ideal for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Design sharp-looking __KEYWORD__ memes. Send to group chats on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Turn moments into funny __KEYWORD__ memes. Share with friends on WhatsApp, Telegram, Discord. Best for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make funny __KEYWORD__ memes with MEMEFA.ST! Send to your crew on WhatsApp, Telegram, Discord. Works across TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit fast and funny __KEYWORD__ memes online. Share on WhatsApp, Telegram, Discord. Built for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Craft __KEYWORD__ memes with ease. Send to friends on WhatsApp, Telegram, Discord. Designed for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Build meme edits that feel right. Share on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create scroll-stopping __KEYWORD__ memes in seconds. Send to group chats on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make cool, clean __KEYWORD__ memes. Share with friends on WhatsApp, Telegram, Discord. Fits TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit funny __KEYWORD__ memes. Send to your squad on WhatsApp, Telegram, Discord. Ideal for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Drop funny __KEYWORD__ memes to your friends. Share on WhatsApp, Telegram, Discord. Format fits TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Craft fast, fun __KEYWORD__ memes. Send to group chats on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Build clean meme formats for __KEYWORD__. Share with friends on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create cool-looking __KEYWORD__ memes. Send to your crew on WhatsApp, Telegram, Discord. Made for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Turn quick ideas into __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. Built for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Post funny __KEYWORD__ memes to friends. Send on WhatsApp, Telegram, Discord. Great for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make feed-friendly __KEYWORD__ memes. Share with friends on WhatsApp, Telegram, Discord. Optimized for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit short and funny __KEYWORD__ memes. Send to group chats on WhatsApp, Telegram, Discord. Perfect fit for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create fast edits for __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Build simple __KEYWORD__ memes that hit. Send to friends on WhatsApp, Telegram, Discord. Works everywhere: TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Craft no-frills __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. Designed for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit funny, format-ready __KEYWORD__ memes. Send to your squad on WhatsApp, Telegram, Discord. For TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Drop quick laughs with __KEYWORD__ memes. Share with friends on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make meme-worthy __KEYWORD__ clips. Send to group chats on WhatsApp, Telegram, Discord. Works best on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create short-form memes with __KEYWORD__. Share on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Design TikTok-ready __KEYWORD__ memes. Send to friends on WhatsApp, Telegram, Discord. Also works for Instagram Reels and YouTube Shorts.',
+ 'Build meme videos around __KEYWORD__. Share on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Craft quick meme edits for __KEYWORD__. Send to your crew on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Generate clean __KEYWORD__ memes fast. Share with friends on WhatsApp, Telegram, Discord. Designed for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make to-the-point __KEYWORD__ memes. Send to group chats on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit compact __KEYWORD__ memes with ease. Share on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create meme videos around __KEYWORD__. Send to friends on WhatsApp, Telegram, Discord. Formats made for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Drop clean meme clips for __KEYWORD__. Share on WhatsApp, Telegram, Discord. Best for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Build creator-friendly __KEYWORD__ memes. Send to your squad on WhatsApp, Telegram, Discord. Ideal for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make short, solid __KEYWORD__ memes. Share with friends on WhatsApp, Telegram, Discord. Works on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit smooth __KEYWORD__ memes in minutes. Send to group chats on WhatsApp, Telegram, Discord. Great for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Craft low-effort __KEYWORD__ memes that hit. Share on WhatsApp, Telegram, Discord. Designed for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create fast-turnaround __KEYWORD__ memes. Send to friends on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Design clean meme edits around __KEYWORD__. Share on WhatsApp, Telegram, Discord. Built for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Build high-replay __KEYWORD__ memes. Send to your crew on WhatsApp, Telegram, Discord. Made for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make meme-worthy short videos with __KEYWORD__. Share with friends on WhatsApp, Telegram, Discord. Great on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Drop funny clips with __KEYWORD__ memes. Send to group chats on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create instantly watchable __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. For TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Design videos around __KEYWORD__ memes. Send to friends on WhatsApp, Telegram, Discord. Works on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Build ready-to-post __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. Best for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Craft on-trend __KEYWORD__ memes fast. Send to your squad on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit memes with __KEYWORD__ that feel current. Share with friends on WhatsApp, Telegram, Discord. Works on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create funny __KEYWORD__ clips that loop well. Send to group chats on WhatsApp, Telegram, Discord. Built for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make clean, short __KEYWORD__ edits. Share on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Drop relatable __KEYWORD__ memes. Send to friends on WhatsApp, Telegram, Discord. Built for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Generate quick meme takes on __KEYWORD__. Share on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Build video memes with __KEYWORD__ in seconds. Send to your crew on WhatsApp, Telegram, Discord. Optimized for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit short meme clips using __KEYWORD__. Share with friends on WhatsApp, Telegram, Discord. Works for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create smooth meme edits with __KEYWORD__. Send to group chats on WhatsApp, Telegram, Discord. For TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make fast content using __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Craft short-form comedy with __KEYWORD__. Send to friends on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Design meme videos fast with __KEYWORD__. Share on WhatsApp, Telegram, Discord. Best for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Build scrollable __KEYWORD__ memes. Send to your squad on WhatsApp, Telegram, Discord. Formats made for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create short, clever __KEYWORD__ memes. Share with friends on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit quick takes with __KEYWORD__ memes. Send to group chats on WhatsApp, Telegram, Discord. Built for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make watchable meme clips using __KEYWORD__. Share on WhatsApp, Telegram, Discord. Works great on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Turn trends into __KEYWORD__ memes. Send to friends on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Build fast meme edits featuring __KEYWORD__. Share on WhatsApp, Telegram, Discord. For TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create funny __KEYWORD__ content. Send to your crew on WhatsApp, Telegram, Discord. Works seamlessly across TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit meme videos using __KEYWORD__. Share with friends on WhatsApp, Telegram, Discord. Match TikTok, Instagram Reels, and YouTube Shorts formats.',
+ 'Drop clean __KEYWORD__ content for short-form feeds. Send to group chats on WhatsApp, Telegram, Discord. Built for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make smart edits with __KEYWORD__. Share on WhatsApp, Telegram, Discord. Optimized for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Generate short-form __KEYWORD__ memes. Send to friends on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Create meme videos around __KEYWORD__. Share on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Edit short and shareable __KEYWORD__ memes. Send to your squad on WhatsApp, Telegram, Discord. Post to TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Make well-timed __KEYWORD__ memes. Share with friends on WhatsApp, Telegram, Discord. Great for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Browse funny __KEYWORD__ memes. Send to group chats on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Find hilarious __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. Made for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Discover trending __KEYWORD__ memes. Send to friends on WhatsApp, Telegram, Discord. Great for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Search cool __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. Built for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Get fresh __KEYWORD__ memes. Send to your crew on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Pick awesome __KEYWORD__ memes. Share with friends on WhatsApp, Telegram, Discord. Works on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Choose top __KEYWORD__ memes. Send to group chats on WhatsApp, Telegram, Discord. Made for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Select viral __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. Great for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Download funny __KEYWORD__ memes. Send to friends on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Save hilarious __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. Built for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Grab trending __KEYWORD__ memes. Send to your squad on WhatsApp, Telegram, Discord. Works on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Access cool __KEYWORD__ memes. Share with friends on WhatsApp, Telegram, Discord. Made for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Use fresh __KEYWORD__ memes. Send to group chats on WhatsApp, Telegram, Discord. Great for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Watch funny __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. Perfect for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'View hilarious __KEYWORD__ memes. Send to friends on WhatsApp, Telegram, Discord. Built for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Stream trending __KEYWORD__ memes. Share on WhatsApp, Telegram, Discord. Works on TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Play cool __KEYWORD__ memes. Send to your crew on WhatsApp, Telegram, Discord. Made for TikTok, Instagram Reels, and YouTube Shorts.',
+ 'Check out fresh __KEYWORD__ memes. Share with friends on WhatsApp, Telegram, Discord. Great for TikTok, Instagram Reels, and YouTube Shorts.',
+ ],
+];
diff --git a/database/migrations/2025_07_16_084731_add_slug_to_meme_medias_table.php b/database/migrations/2025_07_16_084731_add_slug_to_meme_medias_table.php
new file mode 100644
index 0000000..7f2ed61
--- /dev/null
+++ b/database/migrations/2025_07_16_084731_add_slug_to_meme_medias_table.php
@@ -0,0 +1,30 @@
+string('slug')->nullable()->after('name');
+ $table->index('slug');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('meme_medias', function (Blueprint $table) {
+ $table->dropIndex(['slug']);
+ $table->dropColumn('slug');
+ });
+ }
+};
diff --git a/public/memefast-og-template.jpg b/public/memefast-og-template.jpg
new file mode 100644
index 0000000..ee0eebb
Binary files /dev/null and b/public/memefast-og-template.jpg differ
diff --git a/public/robots.txt b/public/robots.txt
index eb05362..902fedf 100644
--- a/public/robots.txt
+++ b/public/robots.txt
@@ -1,2 +1,4 @@
User-agent: *
Disallow:
+
+Sitemap: https://memefast.app/sitemap.xml
diff --git a/resources/fonts/Geist/Geist-Black.ttf b/resources/fonts/Geist/Geist-Black.ttf
new file mode 100644
index 0000000..0bd189d
Binary files /dev/null and b/resources/fonts/Geist/Geist-Black.ttf differ
diff --git a/resources/fonts/Geist/Geist-Bold.ttf b/resources/fonts/Geist/Geist-Bold.ttf
new file mode 100644
index 0000000..5a4ff17
Binary files /dev/null and b/resources/fonts/Geist/Geist-Bold.ttf differ
diff --git a/resources/fonts/Geist/Geist-ExtraBold.ttf b/resources/fonts/Geist/Geist-ExtraBold.ttf
new file mode 100644
index 0000000..6e92452
Binary files /dev/null and b/resources/fonts/Geist/Geist-ExtraBold.ttf differ
diff --git a/resources/fonts/Geist/Geist-ExtraLight.ttf b/resources/fonts/Geist/Geist-ExtraLight.ttf
new file mode 100644
index 0000000..3ed8ac5
Binary files /dev/null and b/resources/fonts/Geist/Geist-ExtraLight.ttf differ
diff --git a/resources/fonts/Geist/Geist-Light.ttf b/resources/fonts/Geist/Geist-Light.ttf
new file mode 100644
index 0000000..ace1682
Binary files /dev/null and b/resources/fonts/Geist/Geist-Light.ttf differ
diff --git a/resources/fonts/Geist/Geist-Medium.ttf b/resources/fonts/Geist/Geist-Medium.ttf
new file mode 100644
index 0000000..fd7d842
Binary files /dev/null and b/resources/fonts/Geist/Geist-Medium.ttf differ
diff --git a/resources/fonts/Geist/Geist-Regular.ttf b/resources/fonts/Geist/Geist-Regular.ttf
new file mode 100644
index 0000000..d61765f
Binary files /dev/null and b/resources/fonts/Geist/Geist-Regular.ttf differ
diff --git a/resources/fonts/Geist/Geist-SemiBold.ttf b/resources/fonts/Geist/Geist-SemiBold.ttf
new file mode 100644
index 0000000..89afbae
Binary files /dev/null and b/resources/fonts/Geist/Geist-SemiBold.ttf differ
diff --git a/resources/fonts/Geist/Geist-Thin.ttf b/resources/fonts/Geist/Geist-Thin.ttf
new file mode 100644
index 0000000..90c136a
Binary files /dev/null and b/resources/fonts/Geist/Geist-Thin.ttf differ
diff --git a/resources/fonts/Geist/Geist-VariableFont_wght.ttf b/resources/fonts/Geist/Geist-VariableFont_wght.ttf
new file mode 100644
index 0000000..ad6f2c5
Binary files /dev/null and b/resources/fonts/Geist/Geist-VariableFont_wght.ttf differ
diff --git a/resources/fonts/Geist/OFL.txt b/resources/fonts/Geist/OFL.txt
new file mode 100644
index 0000000..679a685
--- /dev/null
+++ b/resources/fonts/Geist/OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2024 The Geist Project Authors (https://github.com/vercel/geist-font.git)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/resources/fonts/Geist/README.txt b/resources/fonts/Geist/README.txt
new file mode 100644
index 0000000..92c9c02
--- /dev/null
+++ b/resources/fonts/Geist/README.txt
@@ -0,0 +1,71 @@
+Geist Variable Font
+===================
+
+This download contains Geist as both a variable font and static fonts.
+
+Geist is a variable font with this axis:
+ wght
+
+This means all the styles are contained in a single file:
+ Geist/Geist-VariableFont_wght.ttf
+
+If your app fully supports variable fonts, you can now pick intermediate styles
+that aren’t available as static fonts. Not all apps support variable fonts, and
+in those cases you can use the static font files for Geist:
+ Geist/static/Geist-Thin.ttf
+ Geist/static/Geist-ExtraLight.ttf
+ Geist/static/Geist-Light.ttf
+ Geist/static/Geist-Regular.ttf
+ Geist/static/Geist-Medium.ttf
+ Geist/static/Geist-SemiBold.ttf
+ Geist/static/Geist-Bold.ttf
+ Geist/static/Geist-ExtraBold.ttf
+ Geist/static/Geist-Black.ttf
+
+Get started
+-----------
+
+1. Install the font files you want to use
+
+2. Use your app's font picker to view the font family and all the
+available styles
+
+Learn more about variable fonts
+-------------------------------
+
+ https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
+ https://variablefonts.typenetwork.com
+ https://medium.com/variable-fonts
+
+In desktop apps
+
+ https://theblog.adobe.com/can-variable-fonts-illustrator-cc
+ https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
+
+Online
+
+ https://developers.google.com/fonts/docs/getting_started
+ https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
+ https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
+
+Installing fonts
+
+ MacOS: https://support.apple.com/en-us/HT201749
+ Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
+ Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
+
+Android Apps
+
+ https://developers.google.com/fonts/docs/android
+ https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
+
+License
+-------
+Please read the full license text (OFL.txt) to understand the permissions,
+restrictions and requirements for usage, redistribution, and modification.
+
+You can use them in your products & projects – print or digital,
+commercial or otherwise.
+
+This isn't legal advice, please consider consulting a lawyer and see the full
+license for all details.
diff --git a/resources/js/components/custom/meme-card.tsx b/resources/js/components/custom/meme-card.tsx
new file mode 100644
index 0000000..bdbb052
--- /dev/null
+++ b/resources/js/components/custom/meme-card.tsx
@@ -0,0 +1,89 @@
+import { Badge } from '@/components/ui/badge';
+import { Button } from '@/components/ui/button';
+import { Card } from '@/components/ui/card';
+import { KeywordBadge } from '@/components/ui/keyword-badge';
+import { Link } from '@inertiajs/react';
+import { Edit } from 'lucide-react';
+import { route } from 'ziggy-js';
+
+interface MemeMedia {
+ ids: string;
+ name: string;
+ description: string;
+ keywords: string[];
+ action_keywords: string[];
+ emotion_keywords: string[];
+ misc_keywords: string[];
+ mov_url: string;
+ webm_url: string;
+ gif_url: string;
+ webp_url: string;
+ slug: string;
+}
+
+interface MemeCardProps {
+ meme: MemeMedia;
+ showButton?: boolean;
+ showKeywords?: boolean;
+ className?: string;
+}
+
+export function MemeCard({ meme, showButton = true, showKeywords = true, className = '' }: MemeCardProps) {
+ return (
+
+
+
{meme.name}
+ {showKeywords && (
+
-
-
+
+ + Search through our database of popular meme templates and find the perfect one for your video +
++ {filters.search + ? dynamicDescription || + `Discover ${filters.search} meme templates and create viral content for TikTok, Instagram Reels, and YouTube Shorts.` + : 'Thousands of memes ready for TikTok, Reels, Threads and YouTube Shorts. No signup needed - click any meme to start creating!'} +
++ Request it in our Discord community and we'll add it to the library! +
+{meme.description}
+