feat: statically generate conversion slugs

needs refinement cause building takes forever
This commit is contained in:
Maya 2025-10-15 06:15:50 +03:00
parent d89a343eed
commit 95284a84d4
No known key found for this signature in database
4 changed files with 142 additions and 4 deletions

View File

@ -46,8 +46,8 @@
const color = $derived(
Object.values(colors).find((p) => p.matcher(page.url.pathname)) || {
matcher: () => false,
color: "transparent",
at: 0,
color: "var(--bg-gradient-from)",
at: 100,
},
);
@ -64,9 +64,14 @@
const maskImage = $derived(
`linear-gradient(to top, transparent ${100 - at.current}%, black 100%)`,
);
const showLogo = $derived(
page.url.pathname === "/" ||
/^[\w-]+-[\w-]+$/.test(page.url.pathname.replace(/^\/|\/$/g, "")),
);
</script>
{#if page.url.pathname === "/"}
{#if showLogo}
<div
class="fixed -z-30 top-0 left-0 w-screen h-screen flex items-center justify-center overflow-hidden"
transition:fade={{

View File

@ -37,7 +37,9 @@
{
name: m["navbar.upload"](),
url: "/",
activeMatch: (pathname) => pathname === "/",
activeMatch: (pathname) =>
pathname === "/" ||
/^[\w-]+-[\w-]+$/.test(pathname.replace(/^\/|\/$/g, "")),
icon: UploadIcon,
},
{

View File

@ -0,0 +1,96 @@
import { converters } from "$lib/converters";
import type { EntryGenerator } from "./$types";
// generate conversion pairs at build time (e.g. mkv-mp4) for SEO
export const entries: EntryGenerator = () => {
const seenPairs = new Set<string>();
const addPair = (
fromName: string,
fromSupported: boolean,
toName: string,
toSupported: boolean,
) => {
if (!fromSupported || !toSupported || fromName === toName) return;
const from = fromName.replace(".", "").toLowerCase();
const to = toName.replace(".", "").toLowerCase();
const slug = `${from}-${to}`;
if (!seenPairs.has(slug)) seenPairs.add(slug);
};
// check all conversions (same converter and cross-converter)
for (const fromConverter of converters) {
for (const toConverter of converters) {
const sameConverter = fromConverter.name === toConverter.name;
for (const fromFormat of fromConverter.supportedFormats) {
for (const toFormat of toConverter.supportedFormats) {
// skip if same converter and same format, or if different converter but formats are the same
if (sameConverter && fromFormat.name === toFormat.name)
continue;
if (!sameConverter && fromFormat.name === toFormat.name)
continue;
addPair(
fromFormat.name,
fromFormat.fromSupported,
toFormat.name,
toFormat.toSupported,
);
}
}
}
}
const result = Array.from(seenPairs).map((slug) => ({ formats: slug }));
console.log(`[SEO] generating ${result.length} format conversion routes`);
return result;
};
export const prerender = true;
export const load = ({ params }) => {
const { formats } = params;
// parse the slug (e.g. { from: "mkv", to: "mp4" })
const parts = formats.split("-");
if (parts.length !== 2) {
return {
fromFormat: null,
toFormat: null,
error: "Invalid format slug",
};
}
const [from, to] = parts;
const fromFormat = `.${from}`;
const toFormat = `.${to}`;
let fromValid = false;
let toValid = false;
for (const converter of converters) {
for (const format of converter.supportedFormats) {
if (format.name === fromFormat && format.fromSupported)
fromValid = true;
if (format.name === toFormat && format.toSupported) toValid = true;
}
}
if (!fromValid || !toValid) {
return {
fromFormat: null,
toFormat: null,
error: "Invalid conversion pair",
};
}
return {
fromFormat,
toFormat,
error: null,
};
};

View File

@ -0,0 +1,35 @@
<script lang="ts">
import { error, log } from "$lib/logger";
import Index from "../+page.svelte";
const { data } = $props();
$effect(() => {
if (data.fromFormat && data.toFormat) {
log(["SEO"], `converting from: ${data.fromFormat}`);
log(["SEO"], `converting to: ${data.toFormat}`);
} else if (data.error) {
error(["SEO"], `invalid slug: ${data.error}`);
}
});
</script>
<svelte:head>
{#if data.fromFormat && data.toFormat}
<title
>Convert {data.fromFormat.replace(".", "").toUpperCase()} to {data.toFormat
.replace(".", "")
.toUpperCase()} - VERT</title
>
<meta
name="description"
content="Convert {data.fromFormat
.replace('.', '')
.toUpperCase()} files to {data.toFormat
.replace('.', '')
.toUpperCase()} with VERT. No ads, no tracking, open source, and all processing (other than video) is done on your device."
/>
{/if}
</svelte:head>
<!-- render main upload page -->
<Index />