short-video-maker
Version:
Creates short videos for TikTok, Instagram Reels, and YouTube Shorts using the Model Context Protocol (MCP) and a REST API.
104 lines (103 loc) • 4.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PexelsAPI = void 0;
/* eslint-disable @remotion/deterministic-randomness */
const utils_1 = require("../../components/utils");
const logger_1 = require("../../logger");
const shorts_1 = require("../../types/shorts");
const jokerTerms = ["nature", "globe", "space", "ocean"];
const durationBufferSeconds = 3;
const defaultTimeoutMs = 5000;
const retryTimes = 3;
class PexelsAPI {
API_KEY;
constructor(API_KEY) {
this.API_KEY = API_KEY;
}
async _findVideo(searchTerm, minDurationSeconds, excludeIds, orientation, timeout) {
if (!this.API_KEY) {
throw new Error("API key not set");
}
logger_1.logger.debug({ searchTerm, minDurationSeconds, orientation }, "Searching for video in Pexels API");
const headers = new Headers();
headers.append("Authorization", this.API_KEY);
const response = await fetch(`https://api.pexels.com/videos/search?orientation=${orientation}&size=medium&per_page=80&query=${encodeURIComponent(searchTerm)}`, {
method: "GET",
headers,
redirect: "follow",
signal: AbortSignal.timeout(timeout),
})
.then((res) => res.json())
.catch((error) => {
logger_1.logger.error(error, "Error fetching videos from Pexels API");
throw error;
});
const videos = response.videos;
const { width: requiredVideoWidth, height: requiredVideoHeight } = (0, utils_1.getOrientationConfig)(orientation);
if (!videos || videos.length === 0) {
logger_1.logger.error({ searchTerm, orientation }, "No videos found in Pexels API");
throw new Error("No videos found");
}
// find all the videos that fits the criteria, then select one randomly
const filteredVideos = videos
.map((video) => {
if (excludeIds.includes(video.id)) {
return;
}
if (!video.video_files.length) {
return;
}
// calculate the real duration of the video by converting the FPS to 25
const fps = video.video_files[0].fps;
const duration = fps < 25 ? video.duration * (fps / 25) : video.duration;
if (duration >= minDurationSeconds + durationBufferSeconds) {
for (const file of video.video_files) {
if (file.quality === "hd" &&
file.width === requiredVideoWidth &&
file.height === requiredVideoHeight) {
return {
id: video.id,
url: file.link,
width: file.width,
height: file.height,
};
}
}
}
})
.filter(Boolean);
if (!filteredVideos.length) {
logger_1.logger.error({ searchTerm }, "No videos found in Pexels API");
throw new Error("No videos found");
}
const video = filteredVideos[Math.floor(Math.random() * filteredVideos.length)];
logger_1.logger.debug({ searchTerm, video: video, minDurationSeconds, orientation }, "Found video from Pexels API");
return video;
}
async findVideo(searchTerms, minDurationSeconds, excludeIds = [], orientation = shorts_1.OrientationEnum.portrait, timeout = defaultTimeoutMs, retryCounter = 0) {
// shuffle the search terms to randomize the search order
const shuffledJokerTerms = jokerTerms.sort(() => Math.random() - 0.5);
const shuffledSearchTerms = searchTerms.sort(() => Math.random() - 0.5);
for (const searchTerm of [...shuffledSearchTerms, ...shuffledJokerTerms]) {
try {
return await this._findVideo(searchTerm, minDurationSeconds, excludeIds, orientation, timeout);
}
catch (error) {
if (error instanceof Error &&
error instanceof DOMException &&
error.name === "TimeoutError") {
if (retryCounter < retryTimes) {
logger_1.logger.warn({ searchTerm, retryCounter }, "Timeout error, retrying...");
return await this.findVideo(searchTerms, minDurationSeconds, excludeIds, orientation, timeout, retryCounter + 1);
}
logger_1.logger.error({ searchTerm, retryCounter }, "Timeout error, retry limit reached");
throw error;
}
logger_1.logger.error(error, "Error finding video in Pexels API for term");
}
}
logger_1.logger.error({ searchTerms }, "No videos found in Pexels API for the given terms");
throw new Error("No videos found in Pexels API");
}
}
exports.PexelsAPI = PexelsAPI;