tool($slug); if ($tool === null) { return (new Errors())->notFound(); } $related = service('relatedTools')->forTool($tool); $conversions = service('relatedTools')->conversionsAround($tool); $category = service('finder')->categoryById((int) $tool['category_id']); $guides = $this->guidesFor($tool); // capability flags for honest UX $capabilities = [ 'yt_dlp' => service('pipeline')->youtubeEnabled(), 'binaries' => $this->checkBinaries($tool), ]; $this->seo->title($tool['seo_title']) ->description($tool['seo_description']) ->canonical('/' . $tool['slug']) ->breadcrumbs([ ['label' => $category['name'] ?? 'Tools', 'url' => '/' . ($category['slug'] ?? 'tools')], ['label' => $tool['name']], ]) ->webApplication($tool['name'], (string) $tool['short_description'], $this->appCategory($tool)) ->faqPage(is_array($tool['faqs']) ? $tool['faqs'] : []) ->howTo((string) $tool['name'], is_array($tool['how_to']) ? $tool['how_to'] : []) ->ogImage('/og/' . $tool['slug'] . '.png'); foreach (config('Site')->locales as $locale => $_label) { if ($locale === config('Site')->defaultLocale) { continue; } $this->seo->alternate($locale, '/' . $locale . '/' . $tool['slug']); } $this->seo->alternate('x-default', '/' . $tool['slug']); $this->trackToolView($tool['slug']); return $this->render('tool', [ 'tool' => $tool, 'category' => $category, 'related' => $related, 'conversions' => $conversions, 'guides' => $guides, 'capabilities' => $capabilities, ]); } /** @return list */ private function guidesFor(array $tool): array { $guides = model(\App\Models\GuideModel::class)->published(30); $hits = []; foreach ($guides as $guide) { $linked = json_decode($guide['tool_slugs'] ?? '[]', true) ?: []; if (in_array($tool['slug'], $linked, true)) { $hits[] = $guide; } } return array_slice($hits, 0, 3); } private function checkBinaries(array $tool): array { $ok = []; foreach (($tool['requires_binaries'] ?? []) as $bin) { $path = config('Site')->binaries[$bin] ?? null; $ok[$bin] = $path !== null && is_executable($path); } return $ok; } private function appCategory(array $tool): string { return match (FormatCatalog::family((string) ($tool['primary_output_format'] ?? ''))) { 'video' => 'MultimediaApplication', 'audio' => 'MultimediaApplication', 'image' => 'DesignApplication', default => 'UtilitiesApplication', }; } }