152 lines
6.4 KiB
JavaScript
152 lines
6.4 KiB
JavaScript
import express from "express";
|
|
import { createPoster } from "./common.js";
|
|
import Joi from 'joi';
|
|
|
|
// Define default parameters for the legacy /poster endpoint
|
|
const DEFAULT_WIDTH = 480;
|
|
const DEFAULT_HEIGHT = 320;
|
|
const DEFAULT_COUNT = 4;
|
|
const DEFAULT_ORIENTATION = "horizontal";
|
|
const DEFAULT_SHUFFLE = true;
|
|
const DEFAULT_BACKGROUND_COLOR = "#000000"; // Consistent 6-digit hex
|
|
const DEFAULT_FORMAT = 'png'; // Default format consistent with POST /
|
|
const DEFAULT_LANGUAGE = 'de-DE'; // Default language consistent with POST /
|
|
const DEFAULT_OUTPUT = 'image'; // Default output consistent with POST /
|
|
|
|
const router = express.Router();
|
|
|
|
// --- Validation Schema for POST / ---
|
|
// Defines the expected structure and constraints for the request body.
|
|
const posterRequestSchema = Joi.object({
|
|
// Dimensions and Count
|
|
width: Joi.number().integer().min(1).max(4000).default(DEFAULT_WIDTH)
|
|
.messages({'number.base': "'width' must be a number", 'number.min': "'width' must be at least 1", 'number.max': "'width' cannot exceed 4000"}),
|
|
height: Joi.number().integer().min(1).max(4000).default(DEFAULT_HEIGHT)
|
|
.messages({'number.base': "'height' must be a number", 'number.min': "'height' must be at least 1", 'number.max': "'height' cannot exceed 4000"}),
|
|
count: Joi.number().integer().min(1).max(20).default(DEFAULT_COUNT)
|
|
.messages({'number.base': "'count' must be a number", 'number.min': "'count' must be at least 1", 'number.max': "'count' cannot exceed 20"}),
|
|
|
|
// Layout and Content
|
|
orientation: Joi.string().required().valid('horizontal', 'vertical').default(DEFAULT_ORIENTATION)
|
|
.messages({'any.required': "'orientation' is required", 'any.only': "'orientation' must be either 'horizontal' or 'vertical'"}),
|
|
shuffle: Joi.boolean().default(DEFAULT_SHUFFLE)
|
|
.messages({'boolean.base': "'shuffle' must be a boolean"}),
|
|
language: Joi.string().trim().default(DEFAULT_LANGUAGE) // Added trim()
|
|
.messages({'string.base': "'language' must be a string"}),
|
|
|
|
// Styling and Output
|
|
backgroundColor: Joi.string().trim() // Added trim()
|
|
.default(DEFAULT_BACKGROUND_COLOR) // Use 6-digit hex for consistency
|
|
.regex(/^#([a-fA-F0-9]{3}|[a-fA-F0-9]{6})$/) // Allow 3 or 6 hex digits
|
|
.message("The 'backgroundColor' must be a valid hexadecimal color code (e.g., #000 or #FF0000)"),
|
|
format: Joi.string().valid('png', 'jpeg').default(DEFAULT_FORMAT)
|
|
.messages({'any.only': "'format' must be either 'png' or 'jpeg'"}),
|
|
output: Joi.string().valid('image', 'lvgl', 'lvgl_binary').default(DEFAULT_OUTPUT)
|
|
.messages({'any.only': "'output' must be either 'image', 'lvgl' or 'lvgl_binary'"}),
|
|
});
|
|
|
|
// --- Validation Middleware ---
|
|
// Validates the request body against the posterRequestSchema.
|
|
const validatePosterRequest = (req, res, next) => {
|
|
const { error, value } = posterRequestSchema.validate(req.body, {
|
|
abortEarly: false, // Report all errors, not just the first one
|
|
stripUnknown: true // Remove unknown keys from the validated value
|
|
});
|
|
|
|
if (error) {
|
|
// Log the detailed validation error for debugging
|
|
console.warn('Validation failed:', error.details);
|
|
// Construct a user-friendly error message
|
|
const errorMessages = error.details.map(detail => detail.message).join('. ');
|
|
return res.status(400).send({ error: `Invalid request body: ${errorMessages}` });
|
|
}
|
|
|
|
// Replace req.body with the validated, defaulted, and potentially stripped value.
|
|
// This is a common pattern in Express middleware.
|
|
req.body = value;
|
|
next(); // Proceed to the main route handler
|
|
};
|
|
|
|
// --- Main POST Endpoint ---
|
|
// Handles requests to generate the poster image or LVGL data.
|
|
router.post("/", validatePosterRequest, async (req, res) => {
|
|
// Destructure the validated and defaulted body for clarity
|
|
const {
|
|
width,
|
|
height,
|
|
count,
|
|
orientation,
|
|
shuffle,
|
|
backgroundColor,
|
|
format,
|
|
language,
|
|
output
|
|
} = req.body;
|
|
|
|
try {
|
|
// Delegate the core poster creation logic to the common function.
|
|
// createPoster handles fetching data, drawing, and sending the response.
|
|
await createPoster(
|
|
req, // Pass req for API key access within createPoster
|
|
res,
|
|
width,
|
|
height,
|
|
count,
|
|
orientation,
|
|
shuffle,
|
|
backgroundColor,
|
|
format,
|
|
language,
|
|
output
|
|
);
|
|
} catch (error) {
|
|
// This catch block acts as a final safety net.
|
|
// Although createPoster has internal error handling, this catches
|
|
// potential unexpected issues during its execution.
|
|
console.error("Unhandled error during POST / handler:", error);
|
|
// Ensure response is sent only once
|
|
if (!res.headersSent) {
|
|
res.status(500).send({ error: "An unexpected error occurred while processing your request." });
|
|
}
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @deprecated Use the POST /poster endpoint instead for more flexibility.
|
|
* GET /poster: Generates a movie poster collage using predefined default settings.
|
|
* This endpoint is maintained for backward compatibility.
|
|
*/
|
|
router.get("/", async (req, res) => {
|
|
// Call the common poster creation function with hardcoded legacy defaults.
|
|
// Note: This endpoint does not support customization via query parameters.
|
|
try {
|
|
await createPoster(
|
|
req, // Pass req for API key access within createPoster
|
|
res,
|
|
DEFAULT_WIDTH,
|
|
DEFAULT_HEIGHT,
|
|
DEFAULT_COUNT,
|
|
DEFAULT_ORIENTATION,
|
|
DEFAULT_SHUFFLE,
|
|
DEFAULT_BACKGROUND_COLOR,
|
|
DEFAULT_FORMAT,
|
|
DEFAULT_LANGUAGE,
|
|
DEFAULT_OUTPUT
|
|
);
|
|
// createPoster handles sending the response on success.
|
|
} catch (error) {
|
|
// Although createPoster has internal error handling and sends responses,
|
|
// catch potential unexpected errors during the await or within createPoster
|
|
// if they somehow bypass its internal handling.
|
|
console.error("Error in legacy /cinema endpoint:", error);
|
|
// Ensure a response is sent if headers haven't been sent yet.
|
|
if (!res.headersSent) {
|
|
res.status(500).send({ error: "Failed to generate cinema poster due to an internal error." });
|
|
}
|
|
}
|
|
});
|
|
|
|
// Export the router under the 'cinema' namespace
|
|
export const poster = {
|
|
router, // Use shorthand property name
|
|
}; |