Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions pages/contact.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ eleventyNavigation:
order: 2
blocks:
- type: section-header
title: "Get in Touch"
subtitle: "Want to book EnteR 6.0 for your event? Have questions about the ride? Drop us a message."
intro: |
## Get in Touch

- type: contact_form
Want to book EnteR 6.0 for your event? Have questions about the ride? Drop us a message.

- type: contact-form
content: |
## Book EnteR 6.0

Expand Down
23 changes: 12 additions & 11 deletions pages/gallery.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ meta_title: "Gallery - EnteR 6.0"
meta_description: "Photos and videos of the EnteR 6.0 Extreme fairground ride."
blocks:
- type: video-background
full_width: true
video_id: "fOdSi9Uaxds"
content: |
# Gallery
Expand All @@ -17,14 +16,15 @@ blocks:
video_title: "EnteR 6.0 Gallery"

- type: image-background
full_width: true
image: "src/images/IMG_2835.jpeg"
image_alt: "EnteR 6.0 sound system full setup"
content: " "

- type: section-header
title: "THE SYSTEM"
subtitle: "Built for the road. Designed for impact."
intro: |
## THE SYSTEM

Built for the road. Designed for impact.

- type: image-cards
image_aspect_ratio: "4/3"
Expand All @@ -40,14 +40,15 @@ blocks:
description: "Mid and top end cabinets ready for deployment."

- type: image-background
full_width: true
image: "src/images/IMG_2860.jpeg"
image_alt: "EnteR 6.0 panoramic view"
content: " "

- type: section-header
title: "ON THE ROAD"
subtitle: "From build to showground"
intro: |
## ON THE ROAD

From build to showground

- type: image-cards
image_aspect_ratio: "4/3"
Expand All @@ -66,14 +67,15 @@ blocks:
description: "System assembly on the ground."

- type: image-background
full_width: true
image: "src/images/IMG_2819.jpeg"
image_alt: "EnteR 6.0 system close-up"
content: " "

- type: section-header
title: "THE DETAILS"
subtitle: "Every component purpose-built"
intro: |
## THE DETAILS

Every component purpose-built

- type: image-cards
image_aspect_ratio: "1/1"
Expand All @@ -99,7 +101,6 @@ blocks:
description: "Maximum output at the fair."

- type: image-background
full_width: true
image: "src/images/IMG_0808.jpeg"
image_alt: "EnteR 6.0 system detail"
content: " "
Expand Down
8 changes: 4 additions & 4 deletions pages/home.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ eleventyNavigation:
order: 1
blocks:
- type: video-background
full_width: true
video_id: "https://iframe.mediadelivery.net/embed/588034/72c13f1f-5200-4268-9061-0f45809df741?autoplay=true&loop=true&muted=true&preload=true&responsive=true"
content: |
![Enter 6.0](/assets/logo.png)
video_title: "EnteR 6.0 Extreme Ride"

- type: section-header
title: "THE EXTREME"
subtitle: "Redefining the limits of excitement"
intro: |
## THE EXTREME

Redefining the limits of excitement

- type: features
items:
Expand All @@ -35,7 +36,6 @@ blocks:
description: "Manufactured in the UK by Tivoli Enterprises. Dual hydraulic and mechanical locking on every seat, continuously monitored by the onboard computer. Thorough daily inspections carried out and recorded."

- type: video-background
full_width: true
video_id: "https://iframe.mediadelivery.net/embed/588034/a79f990b-d7d7-4bd0-a7e3-af2c875b1580?autoplay=true&loop=true&muted=true&preload=true&responsive=true"
content: |
## Coming to a fair near you
Expand Down
26 changes: 26 additions & 0 deletions scripts/ci-merge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { resolve } from "node:path";
import { sourceExcludes, templateExcludes } from "./consts.js";
import { fs, mergeTemplateAndSource } from "./utils.js";

const [templateDir, sourceDir, combinedDir] = process.argv.slice(2);

if (!templateDir || !sourceDir || !combinedDir) {
console.error(
"Usage: bun scripts/ci-merge.js <template> <source> <combined>",
);
process.exit(1);
}

const template = resolve(templateDir);
const source = resolve(sourceDir);
const combined = resolve(combinedDir);

console.log(`Merging template and source into ${combined}...`);
fs.mkdir(combined);
mergeTemplateAndSource(template, source, combined, {
delete: true,
templateExcludes,
sourceExcludes,
});

console.log("Merge complete.");
26 changes: 26 additions & 0 deletions scripts/consts.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,28 @@
export const templateRepo = "https://github.com/chobbledotcom/chobble-template";
export const buildDir = ".build";

export const templateExcludes = [
".git",
".direnv",
"node_modules",
"*.md",
"test",
"test-*",
".image-cache",
"images",
"landing-pages",
"instagram-posts",
];

export const sourceExcludes = [
".*",
"*.nix",
"README.md",
"scripts",
"node_modules",
"package*.json",
"bun.lock",
"old_site",
"_site",
...(process.env.PLACEHOLDER_IMAGES === "1" ? ["images"] : []),
];
118 changes: 118 additions & 0 deletions scripts/fetch-instagram-posts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#!/usr/bin/env bun

import { join } from "node:path";
import { exists, fs, loadEnv, path, readJson, write } from "./utils.js";

await loadEnv();

const CONFIG = {
siteConfig: path("_data", "site.json"),
postsDir: path("instagram-posts"),
imagesDir: path("images", "instagram-posts"),
actorId: "shu8hvrXbJbY3Eb9W",
resultsLimit: 100,
};

const formatTimestamp = (iso) =>
iso.replace(/[:.]/g, "-").replace(/-\d{3}Z$/, "Z");

const extractUsername = (instagramUrl) => {
const match = instagramUrl.match(/instagram\.com\/([^/?#]+)/);
return match ? match[1] : null;
};

const fetchPosts = async (profileUrl) => {
const url = `https://api.apify.com/v2/acts/${CONFIG.actorId}/run-sync-get-dataset-items?token=${process.env.APIFY_API_TOKEN}`;

console.log(`Fetching posts for ${profileUrl}...`);

const res = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
directUrls: [profileUrl],
resultsType: "posts",
resultsLimit: CONFIG.resultsLimit,
}),
});

if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);

const results = await res.json();
if (!Array.isArray(results)) throw new Error("Invalid API response format");

return results
.filter((p) => p.timestamp && p.displayUrl && p.url)
.map((p) => ({
date: p.timestamp,
title: p.caption || "",
url: p.url,
thumbnail: p.displayUrl,
}));
};

const downloadImage = async (imageUrl, filepath) => {
const res = await fetch(imageUrl);
if (!res.ok) throw new Error(`Image HTTP ${res.status} for ${imageUrl}`);
await write(filepath, await res.arrayBuffer());
};

const savePost = async (post) => {
const slug = formatTimestamp(post.date);
const jsonPath = join(CONFIG.postsDir, `${slug}.json`);
const imagePath = join(CONFIG.imagesDir, `${slug}.jpg`);

if (await exists(jsonPath)) return false;

await downloadImage(post.thumbnail, imagePath);

const record = {
thumbnail: `/images/instagram-posts/${slug}.jpg`,
title: post.title,
date: post.date,
url: post.url,
};

await write(jsonPath, `${JSON.stringify(record, null, 2)}\n`);
console.log(`${slug}.json`);
return true;
};

const main = async () => {
if (!process.env.APIFY_API_TOKEN) {
console.error("Error: APIFY_API_TOKEN required in .env file");
console.error("Get token: https://console.apify.com/account/integrations");
process.exit(1);
}

if (!(await exists(CONFIG.siteConfig))) {
console.error(`Error: ${CONFIG.siteConfig} not found`);
process.exit(1);
}

const siteConfig = await readJson(CONFIG.siteConfig);
const instagramUrl = siteConfig.socials?.Instagram;
if (!instagramUrl || !extractUsername(instagramUrl)) {
console.error("Error: socials.Instagram missing or invalid in site.json");
process.exit(1);
}

fs.mkdir(CONFIG.postsDir);
fs.mkdir(CONFIG.imagesDir);

const posts = await fetchPosts(instagramUrl);
console.log(`Found ${posts.length} posts`);

const saved = (await Promise.all(posts.map(savePost))).filter(Boolean).length;

console.log(
`\nSaved ${saved} new posts (${posts.length - saved} already existed)`,
);
};

if (import.meta.main) {
main().catch((err) => {
console.error("Error:", err.message);
process.exit(1);
});
}
57 changes: 27 additions & 30 deletions scripts/prepare-dev.js
Original file line number Diff line number Diff line change
@@ -1,41 +1,36 @@
import { join } from "node:path";
import { buildDir, templateRepo } from "./consts.js";
import { bun, find, fs, git, path, root, rsync } from "./utils.js";
import {
buildDir,
sourceExcludes,
templateExcludes,
templateRepo,
} from "./consts.js";
import {
bun,
copyDir,
find,
fs,
git,
mergeTemplateAndSource,
path,
root,
} from "./utils.js";

const build = path(buildDir);
const template = path(buildDir, "template");
const dev = path(buildDir, "dev");
const localTemplate = join(root, "..", "chobble-template");

const templateExcludes = [
".git",
"node_modules",
"*.md",
"test",
"test-*",
".image-cache",
];
const rootExcludes = [
".git",
".direnv",
"*.nix",
"README.md",
buildDir,
"scripts",
"node_modules",
"package*.json",
"bun.lock",
"old_site",
...(process.env.PLACEHOLDER_IMAGES === "1" ? ["images"] : []),
];

export const prep = () => {
console.log("Preparing build...");
fs.mkdir(build);

if (fs.exists(localTemplate)) {
console.log("Using local template from ../chobble-template...");
rsync(localTemplate, template, { delete: true, exclude: [".git", "node_modules"] });
copyDir(localTemplate, template, {
delete: true,
exclude: templateExcludes,
});
} else if (!fs.exists(join(template, ".git"))) {
console.log("Cloning template...");
fs.rm(template);
Expand All @@ -47,8 +42,11 @@ export const prep = () => {
}

find.deleteByExt(dev, ".md");
rsync(template, dev, { delete: true, exclude: templateExcludes });
rsync(root, join(dev, "src"), { exclude: rootExcludes });
mergeTemplateAndSource(template, root, dev, {
delete: true,
templateExcludes,
sourceExcludes,
});

sync();

Expand All @@ -62,10 +60,9 @@ export const prep = () => {
};

export const sync = () => {
rsync(root, join(dev, "src"), {
copyDir(root, join(dev, "src"), {
update: true,
exclude: rootExcludes,
include: ["*/", "**/*.md", "**/*.scss"],
exclude: sourceExcludes,
});
};

Expand Down
Loading
Loading