diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile
index 9f2dfa2c..f8e13a69 100644
--- a/apps/web/Dockerfile
+++ b/apps/web/Dockerfile
@@ -6,6 +6,8 @@ WORKDIR /app
ARG FREESOUND_CLIENT_ID
ARG FREESOUND_API_KEY
+ARG NEXT_PUBLIC_MARBLE_API_URL=https://api.marblecms.com
+ARG MARBLE_WORKSPACE_KEY=build-placeholder
COPY package.json package.json
COPY bun.lock bun.lock
@@ -30,8 +32,8 @@ ENV BETTER_AUTH_SECRET="build-time-secret"
ENV UPSTASH_REDIS_REST_URL="http://localhost:8079"
ENV UPSTASH_REDIS_REST_TOKEN="example_token"
ENV NEXT_PUBLIC_SITE_URL="http://localhost:3000"
-ENV NEXT_PUBLIC_MARBLE_API_URL="https://api.marblecms.com"
-ENV MARBLE_WORKSPACE_KEY="build-placeholder"
+ENV NEXT_PUBLIC_MARBLE_API_URL=$NEXT_PUBLIC_MARBLE_API_URL
+ENV MARBLE_WORKSPACE_KEY=$MARBLE_WORKSPACE_KEY
ENV CLOUDFLARE_ACCOUNT_ID="build-placeholder"
ENV R2_ACCESS_KEY_ID="build-placeholder"
ENV R2_SECRET_ACCESS_KEY="build-placeholder"
diff --git a/apps/web/src/app/blog/page.tsx b/apps/web/src/app/blog/page.tsx
index 7a2b5f97..257a8f23 100644
--- a/apps/web/src/app/blog/page.tsx
+++ b/apps/web/src/app/blog/page.tsx
@@ -18,7 +18,7 @@ export const metadata: Metadata = {
};
export default async function BlogPage() {
- const data = await getPosts();
+ const data = await getPosts().catch(() => null);
if (!data || !data.posts) return
No posts yet
;
return (
diff --git a/apps/web/src/lib/blog/query.ts b/apps/web/src/lib/blog/query.ts
index e6d68bb6..f814eeaf 100644
--- a/apps/web/src/lib/blog/query.ts
+++ b/apps/web/src/lib/blog/query.ts
@@ -1,6 +1,7 @@
import type {
MarbleAuthorList,
MarbleCategoryList,
+ Pagination,
MarblePost,
MarblePostList,
MarbleTagList,
@@ -16,43 +17,98 @@ const url =
process.env.NEXT_PUBLIC_MARBLE_API_URL ?? "https://api.marblecms.com";
const key = process.env.MARBLE_WORKSPACE_KEY ?? "cmd4iw9mm0006l804kwqv0k46";
+const EMPTY_PAGINATION: Pagination = {
+ limit: 0,
+ currpage: 1,
+ nextPage: null,
+ prevPage: null,
+ totalItems: 0,
+ totalPages: 0,
+};
+
+const EMPTY_POSTS: MarblePostList = {
+ posts: [],
+ pagination: EMPTY_PAGINATION,
+};
+
+const EMPTY_TAGS: MarbleTagList = {
+ tags: [],
+ pagination: EMPTY_PAGINATION,
+};
+
+const EMPTY_CATEGORIES: MarbleCategoryList = {
+ categories: [],
+ pagination: EMPTY_PAGINATION,
+};
+
+const EMPTY_AUTHORS: MarbleAuthorList = {
+ authors: [],
+ pagination: EMPTY_PAGINATION,
+};
+
+function isMarbleConfigured() {
+ return !["", "placeholder", "build-placeholder", "your_workspace_key_here"].includes(key);
+}
+
async function fetchFromMarble({
endpoint,
+ fallback,
}: {
endpoint: string;
+ fallback: T;
}): Promise {
+ if (!isMarbleConfigured()) {
+ return fallback;
+ }
+
try {
const response = await fetch(`${url}/${key}/${endpoint}`);
if (!response.ok) {
- throw new Error(
+ console.warn(
`Failed to fetch ${endpoint}: ${response.status} ${response.statusText}`,
);
+ return fallback;
}
return (await response.json()) as T;
} catch (error) {
console.error(`Error fetching ${endpoint}:`, error);
- throw error;
+ return fallback;
}
}
export async function getPosts() {
- return fetchFromMarble({ endpoint: "posts" });
+ return fetchFromMarble({
+ endpoint: "posts",
+ fallback: EMPTY_POSTS,
+ });
}
export async function getTags() {
- return fetchFromMarble({ endpoint: "tags" });
+ return fetchFromMarble({
+ endpoint: "tags",
+ fallback: EMPTY_TAGS,
+ });
}
export async function getSinglePost({ slug }: { slug: string }) {
- return fetchFromMarble({ endpoint: `posts/${slug}` });
+ return fetchFromMarble({
+ endpoint: `posts/${slug}`,
+ fallback: null,
+ });
}
export async function getCategories() {
- return fetchFromMarble({ endpoint: "categories" });
+ return fetchFromMarble({
+ endpoint: "categories",
+ fallback: EMPTY_CATEGORIES,
+ });
}
export async function getAuthors() {
- return fetchFromMarble({ endpoint: "authors" });
+ return fetchFromMarble({
+ endpoint: "authors",
+ fallback: EMPTY_AUTHORS,
+ });
}
export async function processHtmlContent({
diff --git a/apps/web/src/services/renderer/scene-exporter.ts b/apps/web/src/services/renderer/scene-exporter.ts
index a715efe7..c5bad94f 100644
--- a/apps/web/src/services/renderer/scene-exporter.ts
+++ b/apps/web/src/services/renderer/scene-exporter.ts
@@ -100,8 +100,21 @@ export class SceneExporter extends EventEmitter {
let audioSource: AudioBufferSource | null = null;
if (this.shouldIncludeAudio && this.audioBuffer) {
+ let audioCodec: "aac" | "opus" =
+ this.format === "webm" ? "opus" : "aac";
+
+ if (audioCodec === "aac" && typeof AudioEncoder !== "undefined") {
+ const { supported } = await AudioEncoder.isConfigSupported({
+ codec: "mp4a.40.2",
+ sampleRate: this.audioBuffer.sampleRate,
+ numberOfChannels: this.audioBuffer.numberOfChannels,
+ bitrate: 192000,
+ });
+ if (!supported) audioCodec = "opus";
+ }
+
audioSource = new AudioBufferSource({
- codec: this.format === "webm" ? "opus" : "aac",
+ codec: audioCodec,
bitrate: qualityMap[this.quality],
});
output.addAudioTrack(audioSource);
diff --git a/docker-compose.yml b/docker-compose.yml
index ae75391e..a20c2654 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -54,6 +54,8 @@ services:
args:
- FREESOUND_CLIENT_ID=${FREESOUND_CLIENT_ID}
- FREESOUND_API_KEY=${FREESOUND_API_KEY}
+ - NEXT_PUBLIC_MARBLE_API_URL=${NEXT_PUBLIC_MARBLE_API_URL:-https://api.marblecms.com}
+ - MARBLE_WORKSPACE_KEY=${MARBLE_WORKSPACE_KEY:-build-placeholder}
restart: unless-stopped
ports:
- "3100:3000"