UNPKG

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
"use strict"; 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;