--- import { CATEGORIES, PYTHON_SIDECAR_TOOLS, TOOLS } from "@snapotter/shared"; import Footer from "@/components/Footer.astro"; import JsonLd from "@/components/JsonLd.astro"; import Navbar from "@/components/Navbar.astro"; import { TOOL_SEO } from "@/data/tool-seo"; import Base from "@/layouts/Base.astro"; export function getStaticPaths() { return TOOLS.map((tool) => ({ params: { slug: tool.id }, props: { tool } })); } const { tool } = Astro.props; const categoryMap = new Map(CATEGORIES.map((c) => [c.id, c])); const category = categoryMap.get(tool.category); const seo = TOOL_SEO[tool.id]; const related = TOOLS.filter((t) => t.category === tool.category && t.id !== tool.id).slice(0, 4); const catColor = category?.color ?? "#737373"; const isAiTool = (PYTHON_SIDECAR_TOOLS as readonly string[]).includes(tool.id); const toolCount = TOOLS.length; const title = seo ? `${seo.searchTitle} | SnapOtter` : `${tool.name} | SnapOtter`; const description = seo?.longDescription ?? tool.description; const breadcrumbJsonLd = { "@context": "https://schema.org", "@type": "BreadcrumbList", itemListElement: [ { "@type": "ListItem", position: 1, name: "Home", item: "https://snapotter.com" }, { "@type": "ListItem", position: 2, name: "Tools", item: "https://snapotter.com/tools" }, { "@type": "ListItem", position: 3, name: tool.name, item: `https://snapotter.com/tools/${tool.id}`, }, ], }; const softwareJsonLd = { "@context": "https://schema.org", "@type": "WebApplication", name: `SnapOtter ${tool.name}`, description, applicationCategory: "MultimediaApplication", operatingSystem: "Linux, macOS, Windows (via Docker)", browserRequirements: "Requires JavaScript", offers: { "@type": "Offer", price: "0", priceCurrency: "USD" }, license: "https://www.gnu.org/licenses/agpl-3.0.en.html", url: `https://snapotter.com/tools/${tool.id}`, ...(seo?.features && { featureList: seo.features }), isPartOf: { "@type": "SoftwareApplication", name: "SnapOtter", url: "https://snapotter.com" }, }; const modalityLabel = tool.modality === "image" ? "image" : tool.modality === "video" ? "video" : tool.modality === "audio" ? "audio" : "file"; const uploadStep = tool.modality === "image" ? "Drag and drop your image file or click to browse. Supports JPEG, PNG, WebP, AVIF, TIFF, HEIC, and RAW formats." : tool.modality === "video" ? "Drag and drop your video file or click to browse. Supports MP4, MOV, AVI, MKV, WebM, and more." : tool.modality === "audio" ? "Drag and drop your audio file or click to browse. Supports MP3, WAV, FLAC, AAC, OGG, and more." : "Drag and drop your file or click to browse. Supports a wide range of input formats."; const howToJsonLd = { "@context": "https://schema.org", "@type": "HowTo", name: `How to use ${tool.name} in SnapOtter`, description, totalTime: "PT1M", tool: { "@type": "HowToTool", name: "SnapOtter" }, step: [ { "@type": "HowToStep", position: 1, name: "Deploy SnapOtter", text: "Run SnapOtter with a single Docker command: docker run -p 1349:1349 snapotter/snapotter", }, { "@type": "HowToStep", position: 2, name: `Open ${tool.name}`, text: `Navigate to the ${tool.name} tool in SnapOtter's interface.`, }, { "@type": "HowToStep", position: 3, name: `Upload your ${modalityLabel}`, text: uploadStep, }, { "@type": "HowToStep", position: 4, name: "Adjust settings and process", text: `Configure ${tool.name} settings to your needs, then click process. Download your result or continue with another tool.`, }, ], }; const faqJsonLd = seo?.faqs && seo.faqs.length > 0 ? { "@context": "https://schema.org", "@type": "FAQPage", mainEntity: seo.faqs.map((faq) => ({ "@type": "Question", name: faq.q, acceptedAnswer: { "@type": "Answer", text: faq.a }, })), } : null; const allJsonLd = [ breadcrumbJsonLd, softwareJsonLd, howToJsonLd, ...(faqJsonLd ? [faqJsonLd] : []), ]; ---
{category?.name} {isAiTool && ( AI-Powered )}

{tool.name}

{description}

{seo?.features && seo.features.length > 0 && (

Features

    {seo.features.map((feature) => (
  • {feature}
  • ))}
)} {seo?.useCases && seo.useCases.length > 0 && (

What you can do

    {seo.useCases.map((useCase) => (
  • {useCase}
  • ))}
)}

{isAiTool ? "AI that runs on your hardware. No cloud APIs, no usage limits." : "Self-hosted. Your files never leave your network."}

{isAiTool ? `Unlike cloud AI services, SnapOtter's ${tool.name} runs the ML model directly on your server. Your files are processed locally with no data sent to external APIs. No per-file fees, no rate limits, no privacy concerns. Deploy once with Docker and use it as much as you need.` : `SnapOtter runs entirely on your own infrastructure. Files processed with ${tool.name} are never uploaded to third-party servers. Deploy a single Docker container and process files with full privacy, no watermarks, and no usage limits. Open source under AGPL-3.0.`}

{seo?.faqs && seo.faqs.length > 0 && (

Frequently asked questions

{seo.faqs.map((faq) => (
{faq.q}
{faq.a}
))}
)} {related.length > 0 && (

More {category?.name} tools

{related.map((t) => { const relCat = categoryMap.get(t.category); const relColor = relCat?.color ?? "#737373"; return ( {t.name}

{t.description}

Learn more
); })}
)}

Ready to try {tool.name}?

Deploy SnapOtter in under a minute. All {toolCount} tools included. Open source and free forever.