Add lower-corner layouts, custom blur backgrounds, and classic blur fill.
Ship composition families, optional background images for lower-corner templates, and an optional blurred cover fill for classic letterbox.
This commit is contained in:
+63
-13
@@ -16,6 +16,7 @@ import { moveFile, writeUploadedFile } from "../fs-utils";
|
||||
import {
|
||||
ARTIST_MAX,
|
||||
INVALID_LAYOUT_TEMPLATE_MESSAGE,
|
||||
isLowerCornerTemplate,
|
||||
normalizeLayoutSettings,
|
||||
SONG_TITLE_MAX,
|
||||
type LayoutSettings,
|
||||
@@ -56,6 +57,11 @@ export function resolveLayoutFromMetadata(metadata: ItemMetadata): LayoutSetting
|
||||
metadata.layout?.blur_amount ??
|
||||
metadata.blurAmount ??
|
||||
metadata.blur_amount,
|
||||
blurFill:
|
||||
metadata.layout?.blurFill ??
|
||||
(metadata.layout as { blur_fill?: boolean } | null | undefined)?.blur_fill ??
|
||||
metadata.blurFill ??
|
||||
metadata.blur_fill,
|
||||
blurOpacity:
|
||||
metadata.layout?.blurOpacity ??
|
||||
(metadata.layout as { blur_opacity?: number } | null | undefined)?.blur_opacity ??
|
||||
@@ -81,6 +87,11 @@ export function resolveLayoutFromMetadata(metadata: ItemMetadata): LayoutSetting
|
||||
(metadata.layout as { text_offset_y?: number } | null | undefined)?.text_offset_y ??
|
||||
metadata.textOffsetY ??
|
||||
metadata.text_offset_y,
|
||||
titleBold:
|
||||
metadata.layout?.titleBold ??
|
||||
(metadata.layout as { title_bold?: boolean } | null | undefined)?.title_bold ??
|
||||
metadata.titleBold ??
|
||||
metadata.title_bold,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -180,6 +191,18 @@ export async function validateJobPayload(user: { id: string }, body: CreateJobPa
|
||||
}
|
||||
}
|
||||
|
||||
const layoutForBg = resolveLayoutFromMetadata(item.metadata);
|
||||
const bgRaw =
|
||||
item.metadata.backgroundImagePath ?? item.metadata.background_image_path ?? null;
|
||||
if (bgRaw && isLowerCornerTemplate(layoutForBg.template)) {
|
||||
const bgPath = assertPathInUserUploads(user.id, bgRaw);
|
||||
await fs.access(bgPath);
|
||||
const st = await fs.stat(bgPath);
|
||||
if (st.size > limits.maxFileSizeBytes) {
|
||||
return `Background image for ${item.audioFilename} exceeds size limit`;
|
||||
}
|
||||
}
|
||||
|
||||
const wm = normalizeWatermarkSettings(
|
||||
item.metadata.watermark,
|
||||
item.metadata.includeWatermark,
|
||||
@@ -242,20 +265,30 @@ export async function createVideoJob(
|
||||
}
|
||||
|
||||
const imagePath = assertPathInUserUploads(user.id, body.imagePath);
|
||||
const items = body.items.map((item) => ({
|
||||
...item,
|
||||
audioPath: assertPathInUserUploads(user.id, item.audioPath),
|
||||
itemImagePath: item.metadata.imagePath
|
||||
? assertPathInUserUploads(user.id, item.metadata.imagePath)
|
||||
: null,
|
||||
watermarkLogoPath: item.metadata.watermark?.logoPath
|
||||
? assertPathInUserUploads(user.id, item.metadata.watermark.logoPath)
|
||||
: null,
|
||||
watermarkFontPath:
|
||||
item.metadata.watermark?.fontKey === "custom" && item.metadata.watermark?.fontPath
|
||||
? assertPathInUserUploads(user.id, item.metadata.watermark.fontPath)
|
||||
const items = body.items.map((item) => {
|
||||
const layout = resolveLayoutFromMetadata(item.metadata);
|
||||
const bgRaw =
|
||||
item.metadata.backgroundImagePath ?? item.metadata.background_image_path ?? null;
|
||||
const backgroundImagePath =
|
||||
bgRaw && isLowerCornerTemplate(layout.template)
|
||||
? assertPathInUserUploads(user.id, bgRaw)
|
||||
: null;
|
||||
return {
|
||||
...item,
|
||||
audioPath: assertPathInUserUploads(user.id, item.audioPath),
|
||||
itemImagePath: item.metadata.imagePath
|
||||
? assertPathInUserUploads(user.id, item.metadata.imagePath)
|
||||
: null,
|
||||
}));
|
||||
backgroundImagePath,
|
||||
watermarkLogoPath: item.metadata.watermark?.logoPath
|
||||
? assertPathInUserUploads(user.id, item.metadata.watermark.logoPath)
|
||||
: null,
|
||||
watermarkFontPath:
|
||||
item.metadata.watermark?.fontKey === "custom" && item.metadata.watermark?.fontPath
|
||||
? assertPathInUserUploads(user.id, item.metadata.watermark.fontPath)
|
||||
: null,
|
||||
};
|
||||
});
|
||||
|
||||
await reserveQuota(user.id, items.length);
|
||||
|
||||
@@ -298,6 +331,7 @@ export async function createVideoJob(
|
||||
creativeCommons: item.metadata.creativeCommons,
|
||||
includeWatermark: wm.mode !== "none",
|
||||
itemImagePath: item.itemImagePath,
|
||||
backgroundImagePath: item.backgroundImagePath,
|
||||
watermarkMode: wm.mode,
|
||||
watermarkText: wm.mode === "text" ? wm.text?.trim() || null : null,
|
||||
watermarkLogoPath: wm.mode === "logo" ? item.watermarkLogoPath : null,
|
||||
@@ -309,10 +343,12 @@ export async function createVideoJob(
|
||||
watermarkOffsetY: wm.offsetY,
|
||||
artist: item.metadata.artist?.trim().slice(0, ARTIST_MAX) || null,
|
||||
layoutTemplate: layout.template,
|
||||
blurFill: layout.blurFill,
|
||||
blurAmount: layout.blurAmount,
|
||||
blurOpacity: layout.blurOpacity,
|
||||
textPadding: layout.textPadding,
|
||||
titleArtistGap: layout.titleArtistGap,
|
||||
titleBold: layout.titleBold,
|
||||
textOffsetX: layout.textOffsetX,
|
||||
textOffsetY: layout.textOffsetY,
|
||||
playlistId: item.metadata.playlistId?.trim() || null,
|
||||
@@ -353,6 +389,19 @@ export async function createVideoJob(
|
||||
}
|
||||
}
|
||||
|
||||
let newBackgroundImage: string | null = null;
|
||||
if (item.backgroundImagePath) {
|
||||
const src = item.backgroundImagePath;
|
||||
if (moved.has(src)) {
|
||||
newBackgroundImage = moved.get(src)!;
|
||||
} else {
|
||||
const ext = path.extname(src);
|
||||
newBackgroundImage = path.join(jobDir, `${item.id}-bg${ext}`);
|
||||
await moveFile(src, newBackgroundImage);
|
||||
moved.set(src, newBackgroundImage);
|
||||
}
|
||||
}
|
||||
|
||||
let newLogo: string | null = null;
|
||||
if (item.watermarkLogoPath) {
|
||||
const src = item.watermarkLogoPath;
|
||||
@@ -383,6 +432,7 @@ export async function createVideoJob(
|
||||
data: {
|
||||
audioPath: newAudioPath,
|
||||
itemImagePath: newItemImage,
|
||||
backgroundImagePath: newBackgroundImage,
|
||||
watermarkLogoPath: newLogo,
|
||||
watermarkFontPath: newFont,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user