@elizaos/plugin-video 2.0.0-beta.1 → 2.0.3-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +64 -27
- package/build.ts +37 -0
- package/dist/index.d.ts +2 -4
- package/dist/index.js +183 -226
- package/dist/index.js.map +12 -1
- package/dist/services/binaries.d.ts +87 -0
- package/dist/services/video.d.ts +68 -0
- package/package.json +26 -9
- package/registry-entry.json +23 -0
- package/tsup.config.ts +0 -22
package/dist/index.js.map
CHANGED
|
@@ -1 +1,12 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/video.ts","../src/services/binaries.ts","../src/index.ts"],"sourcesContent":["import {\n type IAgentRuntime,\n ITranscriptionService,\n IVideoService,\n type Media,\n type Service,\n ServiceType,\n stringToUuid,\n elizaLogger,\n type VideoDownloadOptions,\n type VideoFormat,\n type VideoInfo,\n type VideoProcessingOptions,\n} from \"@elizaos/core\";\nimport ffmpeg from \"fluent-ffmpeg\";\nimport fs from \"fs\";\nimport { tmpdir } from \"os\";\nimport path from \"path\";\nimport { BinaryResolver } from \"./binaries\";\n\n/** Minimal yt-dlp JSON shape used by this service (fields vary by extractor). */\ninterface YtDlpSubtitleTrack {\n url: string;\n}\n\ninterface YtDlpJson {\n title?: string;\n description?: string;\n channel?: string;\n duration?: number;\n thumbnail?: string;\n view_count?: number;\n upload_date?: string;\n formats?: YtDlpFormatRow[];\n categories?: string[];\n subtitles?: Record<string, YtDlpSubtitleTrack[]>;\n automatic_captions?: Record<string, YtDlpSubtitleTrack[]>;\n}\n\ninterface YtDlpFormatRow {\n format_id?: string;\n url?: string;\n ext?: string;\n quality?: string | number;\n filesize?: number;\n vcodec?: string;\n acodec?: string;\n resolution?: string;\n fps?: number;\n tbr?: number;\n}\n\nexport class VideoService extends IVideoService {\n public readonly capabilityDescription =\n \"Video download, processing, and conversion capabilities\";\n static override readonly serviceType = ServiceType.VIDEO;\n private cacheKey = \"content/video\";\n private dataDir = \"./content_cache\";\n private readonly binaries: BinaryResolver;\n private ffmpegPathConfigured = false;\n\n /** Serialize downloads/processing so cache keys and temp files do not race. */\n private processingChain: Promise<void> = Promise.resolve();\n\n constructor(runtime?: IAgentRuntime, binaries?: BinaryResolver) {\n super(runtime);\n this.binaries = binaries ?? BinaryResolver.instance();\n this.ensureDataDirectoryExists();\n }\n\n static async start(runtime: IAgentRuntime): Promise<Service> {\n const service = new VideoService(runtime);\n await service.initialize(runtime);\n return service;\n }\n\n async initialize(_runtime: IAgentRuntime): Promise<void> {\n await this.configureFfmpeg();\n }\n\n private async configureFfmpeg(): Promise<void> {\n if (this.ffmpegPathConfigured) return;\n const ffmpegPath = await this.binaries.getFfmpegPath();\n if (ffmpegPath) {\n ffmpeg.setFfmpegPath(ffmpegPath);\n elizaLogger.log(`[plugin-video] ffmpeg path: ${ffmpegPath}`);\n } else {\n elizaLogger.warn(\n \"[plugin-video] No ffmpeg binary located via env, PATH, or ffmpeg-static; fluent-ffmpeg will fail at first invocation.\",\n );\n }\n this.ffmpegPathConfigured = true;\n }\n\n async stop(): Promise<void> {\n this.processingChain = Promise.resolve();\n }\n\n // Required abstract methods from IVideoService\n async getVideoInfo(url: string): Promise<VideoInfo> {\n const videoInfo = await this.fetchVideoInfo(url);\n const formats: VideoFormat[] = (videoInfo.formats ?? []).map(\n (f: YtDlpFormatRow) => ({\n formatId: f.format_id ?? \"\",\n url: f.url ?? \"\",\n extension: f.ext ?? \"\",\n quality:\n f.quality !== undefined && f.quality !== \"\"\n ? String(f.quality)\n : \"unknown\",\n fileSize: f.filesize,\n videoCodec: f.vcodec,\n audioCodec: f.acodec,\n resolution: f.resolution,\n fps: f.fps,\n bitrate: f.tbr,\n }),\n );\n return {\n title: videoInfo.title,\n duration: videoInfo.duration,\n url: url,\n thumbnail: videoInfo.thumbnail,\n description: videoInfo.description,\n uploader: videoInfo.channel,\n viewCount: videoInfo.view_count,\n uploadDate: videoInfo.upload_date\n ? new Date(videoInfo.upload_date)\n : undefined,\n formats,\n };\n }\n\n async downloadVideo(\n url: string,\n options?: VideoDownloadOptions,\n ): Promise<string> {\n const videoId = this.getVideoId(url);\n const outputFile =\n options?.outputPath || path.join(this.dataDir, `${videoId}.mp4`);\n\n // if it already exists, return it\n if (fs.existsSync(outputFile)) {\n return outputFile;\n }\n\n try {\n const downloadOptions: Record<string, string | boolean> = {\n verbose: true,\n output: outputFile,\n writeInfoJson: true,\n };\n\n if (options?.format) {\n downloadOptions.format = options.format;\n }\n if (options?.quality) {\n downloadOptions.format = options.quality;\n }\n if (options?.audioOnly) {\n downloadOptions.extractAudio = true;\n downloadOptions.audioFormat = \"mp3\";\n }\n if (options?.videoOnly) {\n downloadOptions.format = \"bestvideo[ext=mp4]/best[ext=mp4]/best\";\n }\n\n await this.binaries.runYtDlp(url, downloadOptions);\n return outputFile;\n } catch (error) {\n elizaLogger.log(\"Error downloading video:\", error);\n throw new Error(\"Failed to download video\");\n }\n }\n\n async extractAudio(videoPath: string, outputPath?: string): Promise<string> {\n const videoId = this.getVideoId(videoPath);\n const audioFile = outputPath || path.join(this.dataDir, `${videoId}.mp3`);\n\n if (fs.existsSync(audioFile)) {\n return audioFile;\n }\n\n await this.configureFfmpeg();\n return new Promise((resolve, reject) => {\n ffmpeg(videoPath)\n .output(audioFile)\n .noVideo()\n .audioCodec(\"libmp3lame\")\n .on(\"end\", () => {\n elizaLogger.log(\"Audio extraction complete\");\n resolve(audioFile);\n })\n .on(\"error\", (err) => {\n elizaLogger.log(\"Error extracting audio:\", err);\n reject(err);\n })\n .run();\n });\n }\n\n async getThumbnail(\n videoPath: string,\n timestamp: number = 1,\n ): Promise<string> {\n const videoId = this.getVideoId(videoPath);\n const thumbnailFile = path.join(this.dataDir, `${videoId}_thumb.jpg`);\n\n if (fs.existsSync(thumbnailFile)) {\n return thumbnailFile;\n }\n\n await this.configureFfmpeg();\n return new Promise((resolve, reject) => {\n ffmpeg(videoPath)\n .screenshots({\n timestamps: [timestamp],\n filename: `${videoId}_thumb.jpg`,\n folder: this.dataDir,\n size: \"320x240\",\n })\n .on(\"end\", () => {\n elizaLogger.log(\"Thumbnail generation complete\");\n resolve(thumbnailFile);\n })\n .on(\"error\", (err) => {\n elizaLogger.log(\"Error generating thumbnail:\", err);\n reject(err);\n });\n });\n }\n\n async convertVideo(\n videoPath: string,\n outputPath: string,\n options?: VideoProcessingOptions,\n ): Promise<string> {\n await this.configureFfmpeg();\n return new Promise((resolve, reject) => {\n let command = ffmpeg(videoPath);\n\n if (options?.startTime) {\n command = command.seekInput(options.startTime);\n }\n\n command = command.output(outputPath);\n\n if (options?.endTime) {\n command = command.duration(options.endTime - (options.startTime || 0));\n }\n\n if (options?.outputFormat) {\n command = command.format(options.outputFormat);\n }\n\n if (options?.resolution) {\n command = command.size(options.resolution);\n }\n\n if (options?.bitrate) {\n command = command.videoBitrate(options.bitrate);\n }\n\n if (options?.framerate) {\n command = command.fps(options.framerate);\n }\n\n if (options?.videoCodec) {\n command = command.videoCodec(options.videoCodec);\n }\n\n if (options?.audioCodec) {\n command = command.audioCodec(options.audioCodec);\n }\n\n command\n .on(\"end\", () => {\n elizaLogger.log(\"Video conversion complete\");\n resolve(outputPath);\n })\n .on(\"error\", (err) => {\n elizaLogger.log(\"Error converting video:\", err);\n reject(err);\n })\n .run();\n });\n }\n\n async getAvailableFormats(url: string): Promise<VideoFormat[]> {\n try {\n const result = await this.binaries.runYtDlp(url, {\n dumpJson: true,\n verbose: true,\n callHome: false,\n noCheckCertificates: true,\n preferFreeFormats: true,\n youtubeSkipDashManifest: true,\n skipDownload: true,\n });\n\n if (\n typeof result === \"object\" &&\n result !== null &&\n \"formats\" in result\n ) {\n const parsed = result as YtDlpJson;\n if (parsed.formats?.length) {\n return parsed.formats.map((format: YtDlpFormatRow) => ({\n formatId: format.format_id ?? \"\",\n url: format.url,\n extension: format.ext,\n quality:\n format.quality !== undefined && format.quality !== \"\"\n ? String(format.quality)\n : \"unknown\",\n fileSize: format.filesize,\n videoCodec: format.vcodec,\n audioCodec: format.acodec,\n resolution: format.resolution,\n fps: format.fps,\n bitrate: format.tbr,\n }));\n }\n }\n\n return [];\n } catch (error) {\n elizaLogger.log(\"Error getting available formats:\", error);\n throw new Error(\"Failed to get available formats\");\n }\n }\n\n private ensureDataDirectoryExists() {\n if (!fs.existsSync(this.dataDir)) {\n fs.mkdirSync(this.dataDir);\n }\n }\n\n public isVideoUrl(url: string): boolean {\n try {\n const { hostname } = new URL(url);\n return (\n hostname === \"youtube.com\" ||\n hostname.endsWith(\".youtube.com\") ||\n hostname === \"youtu.be\" ||\n hostname === \"vimeo.com\" ||\n hostname.endsWith(\".vimeo.com\")\n );\n } catch {\n return false;\n }\n }\n\n public async downloadMedia(url: string): Promise<string> {\n const videoId = this.getVideoId(url);\n const outputFile = path.join(this.dataDir, `${videoId}.mp4`);\n\n // if it already exists, return it\n if (fs.existsSync(outputFile)) {\n return outputFile;\n }\n\n try {\n await this.binaries.runYtDlp(url, {\n verbose: true,\n output: outputFile,\n format: \"bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best\",\n writeInfoJson: true,\n });\n return outputFile;\n } catch (error) {\n elizaLogger.log(\"Error downloading media:\", error);\n throw new Error(\"Failed to download media\");\n }\n }\n\n public async processVideo(\n url: string,\n runtime: IAgentRuntime,\n ): Promise<Media> {\n const run = this.processingChain.then(() =>\n this.processVideoFromUrl(url, runtime),\n );\n this.processingChain = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n\n private async processVideoFromUrl(\n url: string,\n runtime: IAgentRuntime,\n ): Promise<Media> {\n const videoId =\n url.match(\n /(?:youtu\\.be\\/|youtube\\.com(?:\\/embed\\/|\\/v\\/|\\/watch\\?v=|\\/watch\\?.+&v=))([^\\/&?]+)/, // eslint-disable-line\n )?.[1] || \"\";\n const videoUuid = this.getVideoId(videoId);\n const cacheKey = `${this.cacheKey}/${videoUuid}`;\n\n const cached = await runtime.getCache<Media>(cacheKey);\n\n if (cached) {\n elizaLogger.log(\"Returning cached video file\");\n return cached;\n }\n\n elizaLogger.log(\"Cache miss, processing video\");\n elizaLogger.log(\"Fetching video info\");\n const videoInfo = await this.fetchVideoInfo(url);\n elizaLogger.log(\"Getting transcript\");\n const transcript = await this.getTranscript(url, videoInfo, runtime);\n\n const result: Media = {\n id: videoUuid,\n url: url,\n title: videoInfo.title,\n source: videoInfo.channel,\n description: videoInfo.description,\n text: transcript,\n };\n\n await runtime.setCache(cacheKey, result);\n\n return result;\n }\n\n private getVideoId(url: string): string {\n return stringToUuid(url);\n }\n\n async fetchVideoInfo(url: string): Promise<YtDlpJson> {\n if (url.endsWith(\".mp4\") || url.includes(\".mp4?\")) {\n try {\n const response = await fetch(url);\n if (response.ok) {\n // If the URL is a direct link to an MP4 file, return a simplified video info object\n return {\n title: path.basename(url),\n description: \"\",\n channel: \"\",\n };\n }\n } catch (error) {\n elizaLogger.log(\"Error downloading MP4 file:\", error);\n // Fall back to using youtube-dl if direct download fails\n }\n }\n\n try {\n const result = await this.binaries.runYtDlp(url, {\n dumpJson: true,\n verbose: true,\n callHome: false,\n noCheckCertificates: true,\n preferFreeFormats: true,\n youtubeSkipDashManifest: true,\n writeSub: true,\n writeAutoSub: true,\n subLang: \"en\",\n skipDownload: true,\n });\n return result as YtDlpJson;\n } catch (error) {\n elizaLogger.log(\"Error fetching video info:\", error);\n throw new Error(\"Failed to fetch video information\");\n }\n }\n\n private async getTranscript(\n url: string,\n videoInfo: YtDlpJson,\n runtime: IAgentRuntime,\n ): Promise<string> {\n elizaLogger.log(\"Getting transcript\");\n try {\n // Check for manual subtitles\n if (videoInfo.subtitles && videoInfo.subtitles.en) {\n elizaLogger.log(\"Manual subtitles found\");\n const srtContent = await this.downloadSRT(\n videoInfo.subtitles.en[0].url,\n );\n return this.parseSRT(srtContent);\n }\n\n // Check for automatic captions\n if (videoInfo.automatic_captions && videoInfo.automatic_captions.en) {\n elizaLogger.log(\"Automatic captions found\");\n const captionUrl = videoInfo.automatic_captions.en[0].url;\n const captionContent = await this.downloadCaption(captionUrl);\n return this.parseCaption(captionContent);\n }\n\n // Check if it's a music video\n if (videoInfo.categories && videoInfo.categories.includes(\"Music\")) {\n elizaLogger.log(\"Music video detected, no lyrics available\");\n return \"No lyrics available.\";\n }\n\n // Fall back to audio transcription\n elizaLogger.log(\n \"No subtitles or captions found, falling back to audio transcription\",\n );\n return this.transcribeAudio(url, runtime);\n } catch (error) {\n elizaLogger.log(\"Error in getTranscript:\", error);\n throw error;\n }\n }\n\n private async downloadCaption(url: string): Promise<string> {\n elizaLogger.log(\"Downloading caption from:\", url);\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to download caption: ${response.statusText}`);\n }\n return await response.text();\n }\n\n private parseCaption(captionContent: string): string {\n elizaLogger.log(\"Parsing caption\");\n try {\n const jsonContent = JSON.parse(captionContent);\n if (jsonContent.events) {\n return jsonContent.events\n .filter((event) => event.segs)\n .map((event) => event.segs.map((seg) => seg.utf8).join(\"\"))\n .join(\"\")\n .replace(\"\\n\", \" \");\n } else {\n elizaLogger.log(\"Unexpected caption format:\", jsonContent);\n return \"Error: Unable to parse captions\";\n }\n } catch (error) {\n elizaLogger.log(\"Error parsing caption:\", error);\n return \"Error: Unable to parse captions\";\n }\n }\n\n private parseSRT(srtContent: string): string {\n // Simple SRT parser (replace with a more robust solution if needed)\n return srtContent\n .split(\"\\n\\n\")\n .map((block) => block.split(\"\\n\").slice(2).join(\" \"))\n .join(\" \");\n }\n\n private async downloadSRT(url: string): Promise<string> {\n elizaLogger.log(\"downloadSRT\");\n const response = await fetch(url);\n return await response.text();\n }\n\n async transcribeAudio(url: string, runtime: IAgentRuntime): Promise<string> {\n elizaLogger.log(\"Preparing audio for transcription...\");\n const mp4FilePath = path.join(this.dataDir, `${this.getVideoId(url)}.mp4`);\n\n const mp3FilePath = path.join(this.dataDir, `${this.getVideoId(url)}.mp3`);\n\n if (!fs.existsSync(mp3FilePath)) {\n if (fs.existsSync(mp4FilePath)) {\n elizaLogger.log(\"MP4 file found. Converting to MP3...\");\n await this.convertMp4ToMp3(mp4FilePath, mp3FilePath);\n } else {\n elizaLogger.log(\"Downloading audio...\");\n await this.downloadAudio(url, mp3FilePath);\n }\n }\n\n elizaLogger.log(`Audio prepared at ${mp3FilePath}`);\n\n const audioBuffer = fs.readFileSync(mp3FilePath);\n elizaLogger.log(`Audio file size: ${audioBuffer.length} bytes`);\n\n elizaLogger.log(\"Starting transcription...\");\n const startTime = Date.now();\n const transcriptionService = runtime.getService<ITranscriptionService>(\n ServiceType.TRANSCRIPTION,\n );\n\n if (!transcriptionService) {\n throw new Error(\"Transcription service not found\");\n }\n\n const result = await transcriptionService.transcribeAudio(audioBuffer);\n const transcript = result.text;\n\n const endTime = Date.now();\n elizaLogger.log(\n `Transcription completed in ${(endTime - startTime) / 1000} seconds`,\n );\n\n // Don't delete the MP3 file as it might be needed for future use\n return transcript || \"Transcription failed\";\n }\n\n private async convertMp4ToMp3(\n inputPath: string,\n outputPath: string,\n ): Promise<void> {\n await this.configureFfmpeg();\n return new Promise((resolve, reject) => {\n ffmpeg(inputPath)\n .output(outputPath)\n .noVideo()\n .audioCodec(\"libmp3lame\")\n .on(\"end\", () => {\n elizaLogger.log(\"Conversion to MP3 complete\");\n resolve();\n })\n .on(\"error\", (err) => {\n elizaLogger.log(\"Error converting to MP3:\", err);\n reject(err);\n })\n .run();\n });\n }\n\n private async downloadAudio(\n url: string,\n outputFile: string,\n ): Promise<string> {\n elizaLogger.log(\"Downloading audio\");\n outputFile =\n outputFile ?? path.join(this.dataDir, `${this.getVideoId(url)}.mp3`);\n\n try {\n if (url.endsWith(\".mp4\") || url.includes(\".mp4?\")) {\n elizaLogger.log(\n \"Direct MP4 file detected, downloading and converting to MP3\",\n );\n const tempMp4File = path.join(tmpdir(), `${this.getVideoId(url)}.mp4`);\n const response = await fetch(url);\n const arrayBuffer = await response.arrayBuffer();\n const buffer = Buffer.from(arrayBuffer);\n fs.writeFileSync(tempMp4File, buffer);\n\n await this.configureFfmpeg();\n await new Promise<void>((resolve, reject) => {\n ffmpeg(tempMp4File)\n .output(outputFile)\n .noVideo()\n .audioCodec(\"libmp3lame\")\n .on(\"end\", () => {\n fs.unlinkSync(tempMp4File);\n resolve();\n })\n .on(\"error\", (err) => {\n reject(err);\n })\n .run();\n });\n } else {\n elizaLogger.log(\n \"YouTube video detected, downloading audio with youtube-dl\",\n );\n await this.binaries.runYtDlp(url, {\n verbose: true,\n extractAudio: true,\n audioFormat: \"mp3\",\n output: outputFile,\n writeInfoJson: true,\n });\n }\n return outputFile;\n } catch (error) {\n elizaLogger.log(\"Error downloading audio:\", error);\n throw new Error(\"Failed to download audio\");\n }\n }\n}\n","import { createHash } from \"node:crypto\";\nimport {\n createWriteStream,\n constants as fsConstants,\n promises as fsp,\n} from \"node:fs\";\nimport path from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { elizaLogger, resolveStateDir } from \"@elizaos/core\";\nimport youtubeDl from \"youtube-dl-exec\";\n\nexport type YtDlpRunner = (\n url: string,\n flags?: Record<string, string | boolean | number | undefined>,\n opts?: object,\n) => Promise<unknown>;\n\ninterface YtDlpFactory {\n create: (binaryPath: string) => YtDlpRunner;\n}\n\ntype YtDlpModule = YtDlpRunner & YtDlpFactory;\n\nfunction isYtDlpModule(value: unknown): value is YtDlpModule {\n return (\n typeof value === \"function\" &&\n typeof (value as { create?: unknown }).create === \"function\"\n );\n}\n\nif (!isYtDlpModule(youtubeDl)) {\n throw new TypeError(\"youtube-dl-exec did not expose the expected runner API\");\n}\n\nconst ytDlpModule = youtubeDl;\n\nconst YT_DLP_RELEASE_URL =\n \"https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest\";\nconst YT_DLP_UPDATE_THROTTLE_MS = 60 * 60 * 1000;\nconst YT_DLP_META_FILENAME = \"yt-dlp.meta.json\";\n\ninterface GitHubAsset {\n name: string;\n browser_download_url: string;\n size: number;\n}\n\ninterface GitHubRelease {\n tag_name: string;\n name?: string;\n assets: GitHubAsset[];\n}\n\ninterface YtDlpMeta {\n version: string;\n sha256: string;\n assetName: string;\n downloadedAt: number;\n lastUpdateAttemptedAt: number;\n}\n\ntype YtDlpSource = \"env\" | \"path\" | \"cache\" | \"bundled\";\n\nconst EXTRACTOR_BROKEN_PATTERNS: readonly RegExp[] = [\n /Unable to extract/i,\n /Sign in to confirm/i,\n /nsig extraction failed/i,\n /n_param signature/i,\n /Failed to parse JSON/i,\n /HTTP Error 403/i,\n /This video is unavailable/i,\n /Got error: HTTP Error 429/i,\n];\n\nexport interface BinaryResolverOptions {\n binariesDir?: string;\n releaseUrl?: string;\n fetchImpl?: typeof fetch;\n now?: () => number;\n disableAutoUpdate?: boolean;\n preferSystemPath?: boolean;\n updateThrottleMs?: number;\n envOverridePath?: string | null;\n}\n\nexport class BinaryResolver {\n private static _instance: BinaryResolver | null = null;\n\n static instance(): BinaryResolver {\n if (!BinaryResolver._instance)\n BinaryResolver._instance = new BinaryResolver();\n return BinaryResolver._instance;\n }\n\n /** Test hook: drop the singleton so the next instance() returns fresh state. */\n static resetForTests(): void {\n BinaryResolver._instance = null;\n }\n\n private readonly binariesDir: string;\n private readonly releaseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly now: () => number;\n private readonly disableAutoUpdate: boolean;\n private readonly preferSystemPath: boolean;\n private readonly updateThrottleMs: number;\n private readonly envOverridePath: string | null;\n\n private resolvedYtDlpPath: string | null = null;\n private resolvedYtDlpSource: YtDlpSource | null = null;\n private cachedRunner: YtDlpRunner | null = null;\n private resolvedFfmpegPath: string | null | undefined = undefined;\n private updateInFlight: Promise<void> | null = null;\n\n constructor(opts: BinaryResolverOptions = {}) {\n this.binariesDir = opts.binariesDir ?? defaultBinariesDir();\n this.releaseUrl = opts.releaseUrl ?? YT_DLP_RELEASE_URL;\n this.fetchImpl =\n opts.fetchImpl ??\n ((input: RequestInfo | URL, init?: RequestInit) =>\n globalThis.fetch(input, init));\n this.now = opts.now ?? Date.now;\n this.disableAutoUpdate =\n opts.disableAutoUpdate ?? envBool(\"ELIZA_DISABLE_YTDLP_AUTOUPDATE\");\n this.preferSystemPath =\n opts.preferSystemPath ?? envBool(\"ELIZA_YT_DLP_PREFER_PATH\");\n this.updateThrottleMs = opts.updateThrottleMs ?? YT_DLP_UPDATE_THROTTLE_MS;\n this.envOverridePath =\n opts.envOverridePath !== undefined\n ? opts.envOverridePath\n : (process.env.ELIZA_YT_DLP_PATH ?? null);\n }\n\n get cacheDir(): string {\n return this.binariesDir;\n }\n\n get cachedYtDlpPath(): string {\n return path.join(this.binariesDir, ytDlpFileName());\n }\n\n get metaPath(): string {\n return path.join(this.binariesDir, YT_DLP_META_FILENAME);\n }\n\n /**\n * Resolve the yt-dlp binary path. Order:\n * 1. ELIZA_YT_DLP_PATH env override.\n * 2. If ELIZA_YT_DLP_PREFER_PATH=1: system PATH, then cache.\n * 3. Otherwise: cache (download if missing) → PATH fallback.\n */\n async getYtDlpPath(): Promise<string> {\n if (this.resolvedYtDlpPath) return this.resolvedYtDlpPath;\n\n if (this.envOverridePath) {\n if (await isExecutable(this.envOverridePath)) {\n this.resolvedYtDlpPath = this.envOverridePath;\n this.resolvedYtDlpSource = \"env\";\n elizaLogger.log(\n `[plugin-video] Using yt-dlp from env override: ${this.envOverridePath}`,\n );\n return this.envOverridePath;\n }\n elizaLogger.warn(\n `[plugin-video] ELIZA_YT_DLP_PATH=${this.envOverridePath} is not executable; falling through.`,\n );\n }\n\n if (this.preferSystemPath) {\n const sys = await lookupOnPath(\"yt-dlp\");\n if (sys) {\n this.resolvedYtDlpPath = sys;\n this.resolvedYtDlpSource = \"path\";\n elizaLogger.log(`[plugin-video] Using yt-dlp from PATH: ${sys}`);\n return sys;\n }\n }\n\n const cachePath = this.cachedYtDlpPath;\n if (await isExecutable(cachePath)) {\n this.resolvedYtDlpPath = cachePath;\n this.resolvedYtDlpSource = \"cache\";\n elizaLogger.log(\n `[plugin-video] Using yt-dlp from managed cache: ${cachePath}`,\n );\n return cachePath;\n }\n\n const sys = await lookupOnPath(\"yt-dlp\");\n if (sys) {\n this.resolvedYtDlpPath = sys;\n this.resolvedYtDlpSource = \"path\";\n elizaLogger.log(\n `[plugin-video] Using yt-dlp from PATH (cache empty): ${sys}`,\n );\n return sys;\n }\n\n const bundled = await getBundledYtDlpPath();\n if (bundled) {\n this.resolvedYtDlpPath = bundled;\n this.resolvedYtDlpSource = \"bundled\";\n elizaLogger.log(\n `[plugin-video] Using yt-dlp from youtube-dl-exec bundle: ${bundled}`,\n );\n return bundled;\n }\n\n elizaLogger.log(\n \"[plugin-video] No yt-dlp binary found; downloading to managed cache.\",\n );\n await this.downloadYtDlp();\n this.resolvedYtDlpPath = cachePath;\n this.resolvedYtDlpSource = \"cache\";\n return cachePath;\n }\n\n /** Resolution order: ELIZA_FFMPEG_PATH env → system ffmpeg → ffmpeg-static. */\n async getFfmpegPath(): Promise<string | null> {\n if (this.resolvedFfmpegPath !== undefined) return this.resolvedFfmpegPath;\n\n const envPath = process.env.ELIZA_FFMPEG_PATH;\n if (envPath && (await isExecutable(envPath))) {\n this.resolvedFfmpegPath = envPath;\n return envPath;\n }\n\n const sys = await lookupOnPath(\"ffmpeg\");\n if (sys) {\n this.resolvedFfmpegPath = sys;\n return sys;\n }\n\n try {\n const mod: unknown = await import(\"ffmpeg-static\");\n const staticPath =\n typeof mod === \"string\"\n ? mod\n : mod && typeof mod === \"object\" && \"default\" in mod\n ? (mod.default as string | null | undefined)\n : null;\n if (\n typeof staticPath === \"string\" &&\n staticPath.length > 0 &&\n (await isExecutable(staticPath))\n ) {\n this.resolvedFfmpegPath = staticPath;\n return staticPath;\n }\n } catch (err) {\n elizaLogger.warn(\n `[plugin-video] ffmpeg-static not loadable: ${describeError(err)}`,\n );\n }\n\n this.resolvedFfmpegPath = null;\n return null;\n }\n\n /**\n * Build (or reuse) the yt-dlp runner bound to the resolved binary path.\n * The runner mirrors `youtube-dl-exec`'s default callable signature.\n */\n async getYtDlpRunner(): Promise<YtDlpRunner> {\n if (this.cachedRunner) return this.cachedRunner;\n const binPath = await this.getYtDlpPath();\n this.cachedRunner = ytDlpModule.create(binPath);\n return this.cachedRunner;\n }\n\n /**\n * Run yt-dlp with one auto-update + retry attempt on extractor-failure\n * patterns, when the active binary is the managed cache.\n */\n async runYtDlp(\n url: string,\n flags: Record<string, string | boolean | number | undefined>,\n ): Promise<unknown> {\n const runner = await this.getYtDlpRunner();\n try {\n return await runner(url, flags);\n } catch (err) {\n if (!this.shouldRetryWithUpdate(err)) {\n throw err;\n }\n const updated = await this.tryUpdate();\n if (!updated) {\n throw err;\n }\n const refreshed = await this.getYtDlpRunner();\n return await refreshed(url, flags);\n }\n }\n\n private shouldRetryWithUpdate(err: unknown): boolean {\n if (this.disableAutoUpdate) return false;\n // Auto-update fires only when we own the binary lifecycle (managed cache\n // or the bundled-with-youtube-dl-exec copy). User-managed binaries (env\n // override, system PATH) are out of scope — we don't touch homebrew.\n if (\n this.resolvedYtDlpSource !== \"cache\" &&\n this.resolvedYtDlpSource !== \"bundled\"\n ) {\n return false;\n }\n const msg = errorMessage(err);\n return EXTRACTOR_BROKEN_PATTERNS.some((p) => p.test(msg));\n }\n\n /**\n * Run a yt-dlp update attempt, throttled to once per `updateThrottleMs`.\n * Returns true iff a fresh binary was successfully installed.\n */\n private async tryUpdate(): Promise<boolean> {\n if (this.updateInFlight) {\n await this.updateInFlight;\n return true;\n }\n\n const meta = await this.readMeta();\n // No metadata yet means we have never attempted an update; first failure\n // should always be allowed to try.\n if (meta) {\n const sinceLast = this.now() - meta.lastUpdateAttemptedAt;\n if (sinceLast < this.updateThrottleMs) {\n elizaLogger.warn(\n `[plugin-video] yt-dlp update throttled (last attempt ${Math.floor(sinceLast / 1000)}s ago).`,\n );\n return false;\n }\n }\n\n const job = (async () => {\n await this.touchUpdateAttempt(meta);\n await this.downloadYtDlp();\n this.resetRunnerCache();\n })();\n this.updateInFlight = job;\n try {\n await job;\n return true;\n } catch (err) {\n elizaLogger.error(\n `[plugin-video] yt-dlp update failed: ${describeError(err)}`,\n );\n return false;\n } finally {\n this.updateInFlight = null;\n }\n }\n\n private resetRunnerCache(): void {\n this.cachedRunner = null;\n this.resolvedYtDlpPath = null;\n this.resolvedYtDlpSource = null;\n }\n\n /** Force a fresh yt-dlp download regardless of throttle. Test/admin hook. */\n async forceUpdateYtDlp(): Promise<{ version: string; path: string }> {\n const meta = await this.downloadYtDlp();\n this.resetRunnerCache();\n await this.getYtDlpPath();\n return { version: meta.version, path: this.cachedYtDlpPath };\n }\n\n private async readMeta(): Promise<YtDlpMeta | null> {\n try {\n const raw = await fsp.readFile(this.metaPath, \"utf8\");\n return JSON.parse(raw) as YtDlpMeta;\n } catch {\n return null;\n }\n }\n\n private async writeMeta(meta: YtDlpMeta): Promise<void> {\n await fsp.mkdir(this.binariesDir, { recursive: true });\n await fsp.writeFile(this.metaPath, JSON.stringify(meta, null, 2));\n }\n\n private async touchUpdateAttempt(prev: YtDlpMeta | null): Promise<void> {\n const next: YtDlpMeta = prev\n ? { ...prev, lastUpdateAttemptedAt: this.now() }\n : {\n version: \"\",\n sha256: \"\",\n assetName: \"\",\n downloadedAt: 0,\n lastUpdateAttemptedAt: this.now(),\n };\n await this.writeMeta(next);\n }\n\n /**\n * Download the latest yt-dlp release for this platform, verify the SHA256\n * against the published `SHA2-256SUMS`, and atomically replace the cached\n * binary. Returns the new metadata on success; throws on any failure.\n */\n async downloadYtDlp(): Promise<YtDlpMeta> {\n const release = await this.fetchRelease();\n const assetName = ytDlpAssetName();\n const asset = release.assets.find((a) => a.name === assetName);\n if (!asset) {\n throw new Error(\n `yt-dlp release ${release.tag_name} has no asset named '${assetName}'`,\n );\n }\n const sumsAsset = release.assets.find((a) => a.name === \"SHA2-256SUMS\");\n if (!sumsAsset) {\n throw new Error(\n `yt-dlp release ${release.tag_name} has no SHA2-256SUMS asset`,\n );\n }\n\n const expectedSha = await this.fetchExpectedSha(\n sumsAsset.browser_download_url,\n assetName,\n );\n\n await fsp.mkdir(this.binariesDir, { recursive: true });\n const tmpPath = `${this.cachedYtDlpPath}.tmp.${process.pid}.${Date.now()}`;\n await this.downloadToFile(asset.browser_download_url, tmpPath);\n\n const actualSha = await sha256OfFile(tmpPath);\n if (actualSha !== expectedSha) {\n await safeUnlink(tmpPath);\n throw new Error(\n `yt-dlp SHA256 mismatch: expected ${expectedSha}, got ${actualSha}`,\n );\n }\n\n await fsp.chmod(tmpPath, 0o755);\n await fsp.rename(tmpPath, this.cachedYtDlpPath);\n\n const meta: YtDlpMeta = {\n version: release.tag_name,\n sha256: expectedSha,\n assetName,\n downloadedAt: this.now(),\n lastUpdateAttemptedAt: this.now(),\n };\n await this.writeMeta(meta);\n elizaLogger.log(\n `[plugin-video] yt-dlp ${release.tag_name} installed at ${this.cachedYtDlpPath}`,\n );\n return meta;\n }\n\n private async fetchRelease(): Promise<GitHubRelease> {\n const res = await this.fetchImpl(this.releaseUrl, {\n headers: { Accept: \"application/vnd.github+json\" },\n });\n if (!res.ok) {\n throw new Error(\n `yt-dlp release fetch failed: ${res.status} ${res.statusText}`,\n );\n }\n return (await res.json()) as GitHubRelease;\n }\n\n private async fetchExpectedSha(\n sumsUrl: string,\n assetName: string,\n ): Promise<string> {\n const res = await this.fetchImpl(sumsUrl);\n if (!res.ok) {\n throw new Error(\n `SHA2-256SUMS fetch failed: ${res.status} ${res.statusText}`,\n );\n }\n const text = await res.text();\n for (const line of text.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n const parts = trimmed.split(/\\s+/);\n if (parts.length >= 2 && parts[parts.length - 1] === assetName) {\n return parts[0].toLowerCase();\n }\n }\n throw new Error(`SHA2-256SUMS missing entry for ${assetName}`);\n }\n\n private async downloadToFile(url: string, dest: string): Promise<void> {\n const res = await this.fetchImpl(url);\n if (!res.ok || !res.body) {\n throw new Error(\n `yt-dlp binary fetch failed: ${res.status} ${res.statusText}`,\n );\n }\n const nodeStream = Readable.fromWeb(\n res.body as Parameters<typeof Readable.fromWeb>[0],\n );\n await pipeline(nodeStream, createWriteStream(dest));\n }\n}\n\nfunction defaultBinariesDir(): string {\n const explicit = process.env.ELIZA_BINARIES_DIR;\n if (explicit) return explicit;\n return path.join(resolveStateDir(), \"binaries\");\n}\n\nexport function ytDlpAssetName(): string {\n const platform = process.platform;\n const arch = process.arch;\n if (platform === \"darwin\") return \"yt-dlp_macos\";\n if (platform === \"win32\")\n return arch === \"ia32\" ? \"yt-dlp_x86.exe\" : \"yt-dlp.exe\";\n if (platform === \"linux\") {\n if (arch === \"arm64\") return \"yt-dlp_linux_aarch64\";\n if (arch === \"arm\") return \"yt-dlp_linux_armv7l\";\n return \"yt-dlp_linux\";\n }\n throw new Error(`Unsupported platform for yt-dlp: ${platform}/${arch}`);\n}\n\nexport function ytDlpFileName(): string {\n return process.platform === \"win32\" ? \"yt-dlp.exe\" : \"yt-dlp\";\n}\n\nasync function isExecutable(p: string): Promise<boolean> {\n try {\n await fsp.access(p, fsConstants.X_OK);\n const st = await fsp.stat(p);\n return st.isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Look for the yt-dlp binary that `youtube-dl-exec`'s postinstall ships into\n * its own `bin/` directory. Tries the package's internal constants first\n * (which the wrapper uses by default), then falls back to a hardcoded\n * relative path. Returns the path only if the binary is actually executable.\n */\nasync function getBundledYtDlpPath(): Promise<string | null> {\n try {\n const constantsModule = (await import(\n \"youtube-dl-exec/src/constants.js\" as string\n )) as { default?: { YOUTUBE_DL_PATH?: string } } & {\n YOUTUBE_DL_PATH?: string;\n };\n const fromConstants =\n constantsModule.default?.YOUTUBE_DL_PATH ??\n constantsModule.YOUTUBE_DL_PATH;\n if (\n typeof fromConstants === \"string\" &&\n fromConstants.length > 0 &&\n (await isExecutable(fromConstants))\n ) {\n return fromConstants;\n }\n } catch {\n /* package internals unavailable; fall through to relative resolve */\n }\n try {\n const { createRequire } = await import(\"node:module\");\n const req = createRequire(import.meta.url);\n const pkgPath = req.resolve(\"youtube-dl-exec/package.json\");\n const candidate = path.join(path.dirname(pkgPath), \"bin\", ytDlpFileName());\n if (await isExecutable(candidate)) return candidate;\n } catch {\n /* youtube-dl-exec not installed in this tree */\n }\n return null;\n}\n\nasync function lookupOnPath(name: string): Promise<string | null> {\n const pathEnv = process.env.PATH;\n if (!pathEnv) return null;\n const exts =\n process.platform === \"win32\"\n ? (process.env.PATHEXT ?? \".EXE;.CMD;.BAT\")\n .split(\";\")\n .map((e) => e.toLowerCase())\n : [\"\"];\n for (const dir of pathEnv.split(path.delimiter)) {\n if (!dir) continue;\n for (const ext of exts) {\n const candidate = path.join(dir, `${name}${ext}`);\n if (await isExecutable(candidate)) {\n return candidate;\n }\n }\n }\n return null;\n}\n\nasync function sha256OfFile(p: string): Promise<string> {\n const hash = createHash(\"sha256\");\n const fd = await fsp.open(p, \"r\");\n try {\n const stream = fd.createReadStream();\n for await (const chunk of stream) hash.update(chunk as Buffer);\n } finally {\n await fd.close();\n }\n return hash.digest(\"hex\");\n}\n\nasync function safeUnlink(p: string): Promise<void> {\n try {\n await fsp.unlink(p);\n } catch {\n /* swallow */\n }\n}\n\nfunction envBool(name: string): boolean {\n const v = process.env[name];\n if (!v) return false;\n return v === \"1\" || v.toLowerCase() === \"true\";\n}\n\nfunction errorMessage(err: unknown): string {\n if (!err) return \"\";\n if (typeof err === \"string\") return err;\n const anyErr = err as { stderr?: unknown; message?: unknown };\n if (typeof anyErr.stderr === \"string\" && anyErr.stderr.length > 0)\n return anyErr.stderr;\n if (typeof anyErr.message === \"string\") return anyErr.message;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n}\n\nfunction describeError(err: unknown): string {\n return errorMessage(err) || \"(no message)\";\n}\n","import { Plugin } from \"@elizaos/core\";\nimport { VideoService } from \"./services/video\";\n\nconst videoPlugin: Plugin = {\n name: \"video\",\n description: \"Video processing and transcription capabilities\",\n services: [VideoService],\n actions: [],\n providers: [],\n routes: [],\n};\n\nexport default videoPlugin;\n"],"mappings":"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","names":["elizaLogger","path","sys","elizaLogger","path"]}
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/services/video.ts", "../src/services/binaries.ts", "../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/// <reference path=\"../types/fluent-ffmpeg.d.ts\" />\n\nimport fs from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport {\n elizaLogger,\n type IAgentRuntime,\n type ITranscriptionService,\n IVideoService,\n type Media,\n type Service,\n ServiceType,\n stringToUuid,\n type VideoDownloadOptions,\n type VideoFormat,\n type VideoInfo,\n type VideoProcessingOptions,\n} from \"@elizaos/core\";\nimport ffmpeg from \"fluent-ffmpeg\";\nimport { BinaryResolver } from \"./binaries\";\n\n/** Minimal yt-dlp JSON shape used by this service (fields vary by extractor). */\ninterface YtDlpSubtitleTrack {\n url: string;\n}\n\ninterface YtDlpJson {\n title?: string;\n description?: string;\n channel?: string;\n duration?: number;\n thumbnail?: string;\n view_count?: number;\n upload_date?: string;\n formats?: YtDlpFormatRow[];\n categories?: string[];\n subtitles?: Record<string, YtDlpSubtitleTrack[]>;\n automatic_captions?: Record<string, YtDlpSubtitleTrack[]>;\n}\n\ninterface YtDlpFormatRow {\n format_id?: string;\n url?: string;\n ext?: string;\n quality?: string | number;\n filesize?: number;\n vcodec?: string;\n acodec?: string;\n resolution?: string;\n fps?: number;\n tbr?: number;\n}\n\ninterface CaptionSegment {\n utf8?: string;\n}\n\ninterface CaptionEvent {\n segs?: CaptionSegment[];\n}\n\ninterface CaptionJson {\n events?: CaptionEvent[];\n}\n\nfunction loggableError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction parseYtDlpUploadDate(value: string | undefined): Date | undefined {\n if (!value) return undefined;\n const compact = value.match(/^(\\d{4})(\\d{2})(\\d{2})$/);\n if (compact) {\n const [, year, month, day] = compact;\n return new Date(`${year}-${month}-${day}T00:00:00.000Z`);\n }\n const parsed = new Date(value);\n return Number.isNaN(parsed.getTime()) ? undefined : parsed;\n}\n\nexport class VideoService extends IVideoService {\n public readonly capabilityDescription =\n \"Video download, processing, and conversion capabilities\";\n static override readonly serviceType = ServiceType.VIDEO;\n private cacheKey = \"content/video\";\n private dataDir = \"./content_cache\";\n private readonly binaries: BinaryResolver;\n private ffmpegPathConfigured = false;\n\n /** Serialize downloads/processing so cache keys and temp files do not race. */\n private processingChain: Promise<void> = Promise.resolve();\n\n constructor(runtime?: IAgentRuntime, binaries?: BinaryResolver) {\n super(runtime);\n this.binaries = binaries ?? BinaryResolver.instance();\n this.ensureDataDirectoryExists();\n }\n\n static async start(runtime: IAgentRuntime): Promise<Service> {\n const service = new VideoService(runtime);\n await service.initialize(runtime);\n return service;\n }\n\n async initialize(_runtime: IAgentRuntime): Promise<void> {\n await this.configureFfmpeg();\n }\n\n private async configureFfmpeg(): Promise<void> {\n if (this.ffmpegPathConfigured) return;\n const ffmpegPath = await this.binaries.getFfmpegPath();\n if (ffmpegPath) {\n ffmpeg.setFfmpegPath(ffmpegPath);\n elizaLogger.log(`[plugin-video] ffmpeg path: ${ffmpegPath}`);\n } else {\n elizaLogger.warn(\n \"[plugin-video] No ffmpeg binary located via env, PATH, or ffmpeg-static; fluent-ffmpeg will fail at first invocation.\",\n );\n }\n this.ffmpegPathConfigured = true;\n }\n\n async stop(): Promise<void> {\n this.processingChain = Promise.resolve();\n }\n\n // Required abstract methods from IVideoService\n async getVideoInfo(url: string): Promise<VideoInfo> {\n const videoInfo = await this.fetchVideoInfo(url);\n const formats: VideoFormat[] = (videoInfo.formats ?? []).map(\n (f: YtDlpFormatRow) => ({\n formatId: f.format_id ?? \"\",\n url: f.url ?? \"\",\n extension: f.ext ?? \"\",\n quality:\n f.quality !== undefined && f.quality !== \"\"\n ? String(f.quality)\n : \"unknown\",\n fileSize: f.filesize,\n videoCodec: f.vcodec,\n audioCodec: f.acodec,\n resolution: f.resolution,\n fps: f.fps,\n bitrate: f.tbr,\n }),\n );\n return {\n title: videoInfo.title,\n duration: videoInfo.duration,\n url: url,\n thumbnail: videoInfo.thumbnail,\n description: videoInfo.description,\n uploader: videoInfo.channel,\n viewCount: videoInfo.view_count,\n uploadDate: parseYtDlpUploadDate(videoInfo.upload_date),\n formats,\n };\n }\n\n async downloadVideo(\n url: string,\n options?: VideoDownloadOptions,\n ): Promise<string> {\n const videoId = this.getVideoId(url);\n const outputFile =\n options?.outputPath || path.join(this.dataDir, `${videoId}.mp4`);\n\n // if it already exists, return it\n if (fs.existsSync(outputFile)) {\n return outputFile;\n }\n\n try {\n const downloadOptions: Record<string, string | boolean> = {\n verbose: true,\n output: outputFile,\n writeInfoJson: true,\n };\n\n if (options?.format) {\n downloadOptions.format = options.format;\n }\n if (options?.quality) {\n downloadOptions.format = options.quality;\n }\n if (options?.audioOnly) {\n downloadOptions.extractAudio = true;\n downloadOptions.audioFormat = \"mp3\";\n }\n if (options?.videoOnly) {\n downloadOptions.format = \"bestvideo[ext=mp4]/best[ext=mp4]/best\";\n }\n\n await this.binaries.runYtDlp(url, downloadOptions);\n return outputFile;\n } catch (error) {\n elizaLogger.log(\"Error downloading video:\", loggableError(error));\n throw new Error(\"Failed to download video\");\n }\n }\n\n async extractAudio(videoPath: string, outputPath?: string): Promise<string> {\n const videoId = this.getVideoId(videoPath);\n const audioFile = outputPath || path.join(this.dataDir, `${videoId}.mp3`);\n\n if (fs.existsSync(audioFile)) {\n return audioFile;\n }\n\n await this.configureFfmpeg();\n return new Promise((resolve, reject) => {\n ffmpeg(videoPath)\n .output(audioFile)\n .noVideo()\n .audioCodec(\"libmp3lame\")\n .on(\"end\", () => {\n elizaLogger.log(\"Audio extraction complete\");\n resolve(audioFile);\n })\n .on(\"error\", (err) => {\n elizaLogger.log(\"Error extracting audio:\", loggableError(err));\n reject(err);\n })\n .run();\n });\n }\n\n async getThumbnail(\n videoPath: string,\n timestamp: number = 1,\n ): Promise<string> {\n const videoId = this.getVideoId(videoPath);\n const thumbnailFile = path.join(this.dataDir, `${videoId}_thumb.jpg`);\n\n if (fs.existsSync(thumbnailFile)) {\n return thumbnailFile;\n }\n\n await this.configureFfmpeg();\n return new Promise((resolve, reject) => {\n ffmpeg(videoPath)\n .screenshots({\n timestamps: [timestamp],\n filename: `${videoId}_thumb.jpg`,\n folder: this.dataDir,\n size: \"320x240\",\n })\n .on(\"end\", () => {\n elizaLogger.log(\"Thumbnail generation complete\");\n resolve(thumbnailFile);\n })\n .on(\"error\", (err) => {\n elizaLogger.log(\"Error generating thumbnail:\", loggableError(err));\n reject(err);\n });\n });\n }\n\n async convertVideo(\n videoPath: string,\n outputPath: string,\n options?: VideoProcessingOptions,\n ): Promise<string> {\n await this.configureFfmpeg();\n return new Promise((resolve, reject) => {\n let command = ffmpeg(videoPath);\n\n if (options?.startTime) {\n command = command.seekInput(options.startTime);\n }\n\n command = command.output(outputPath);\n\n if (options?.endTime) {\n command = command.duration(options.endTime - (options.startTime || 0));\n }\n\n if (options?.outputFormat) {\n command = command.format(options.outputFormat);\n }\n\n if (options?.resolution) {\n command = command.size(options.resolution);\n }\n\n if (options?.bitrate) {\n command = command.videoBitrate(options.bitrate);\n }\n\n if (options?.framerate) {\n command = command.fps(options.framerate);\n }\n\n if (options?.videoCodec) {\n command = command.videoCodec(options.videoCodec);\n }\n\n if (options?.audioCodec) {\n command = command.audioCodec(options.audioCodec);\n }\n\n command\n .on(\"end\", () => {\n elizaLogger.log(\"Video conversion complete\");\n resolve(outputPath);\n })\n .on(\"error\", (err) => {\n elizaLogger.log(\"Error converting video:\", loggableError(err));\n reject(err);\n })\n .run();\n });\n }\n\n async getAvailableFormats(url: string): Promise<VideoFormat[]> {\n try {\n const result = await this.binaries.runYtDlp(url, {\n dumpJson: true,\n verbose: true,\n callHome: false,\n noCheckCertificates: true,\n preferFreeFormats: true,\n youtubeSkipDashManifest: true,\n skipDownload: true,\n });\n\n if (\n typeof result === \"object\" &&\n result !== null &&\n \"formats\" in result\n ) {\n const parsed = result as YtDlpJson;\n if (parsed.formats?.length) {\n return parsed.formats.map((format: YtDlpFormatRow) => ({\n formatId: format.format_id ?? \"\",\n url: format.url ?? \"\",\n extension: format.ext ?? \"\",\n quality:\n format.quality !== undefined && format.quality !== \"\"\n ? String(format.quality)\n : \"unknown\",\n fileSize: format.filesize,\n videoCodec: format.vcodec,\n audioCodec: format.acodec,\n resolution: format.resolution,\n fps: format.fps,\n bitrate: format.tbr,\n }));\n }\n }\n\n return [];\n } catch (error) {\n elizaLogger.log(\"Error getting available formats:\", loggableError(error));\n throw new Error(\"Failed to get available formats\");\n }\n }\n\n private ensureDataDirectoryExists() {\n if (!fs.existsSync(this.dataDir)) {\n fs.mkdirSync(this.dataDir);\n }\n }\n\n public isVideoUrl(url: string): boolean {\n try {\n const { hostname } = new URL(url);\n return (\n hostname === \"youtube.com\" ||\n hostname.endsWith(\".youtube.com\") ||\n hostname === \"youtu.be\" ||\n hostname === \"vimeo.com\" ||\n hostname.endsWith(\".vimeo.com\")\n );\n } catch {\n return false;\n }\n }\n\n public async downloadMedia(url: string): Promise<string> {\n const videoId = this.getVideoId(url);\n const outputFile = path.join(this.dataDir, `${videoId}.mp4`);\n\n // if it already exists, return it\n if (fs.existsSync(outputFile)) {\n return outputFile;\n }\n\n try {\n await this.binaries.runYtDlp(url, {\n verbose: true,\n output: outputFile,\n format: \"bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best\",\n writeInfoJson: true,\n });\n return outputFile;\n } catch (error) {\n elizaLogger.log(\"Error downloading media:\", loggableError(error));\n throw new Error(\"Failed to download media\");\n }\n }\n\n public async processVideo(\n url: string,\n runtime: IAgentRuntime,\n ): Promise<Media> {\n const run = this.processingChain.then(() =>\n this.processVideoFromUrl(url, runtime),\n );\n this.processingChain = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n\n private async processVideoFromUrl(\n url: string,\n runtime: IAgentRuntime,\n ): Promise<Media> {\n const extractedVideoId =\n url.match(\n /(?:youtu\\.be\\/|youtube\\.com(?:\\/embed\\/|\\/v\\/|\\/watch\\?v=|\\/watch\\?.+&v=))([^/&?]+)/, // eslint-disable-line\n )?.[1] || url;\n const videoUuid = this.getVideoId(extractedVideoId);\n const cacheKey = `${this.cacheKey}/${videoUuid}`;\n\n const cached = await runtime.getCache<Media>(cacheKey);\n\n if (cached) {\n elizaLogger.log(\"Returning cached video file\");\n return cached;\n }\n\n elizaLogger.log(\"Cache miss, processing video\");\n elizaLogger.log(\"Fetching video info\");\n const videoInfo = await this.fetchVideoInfo(url);\n elizaLogger.log(\"Getting transcript\");\n const transcript = await this.getTranscript(url, videoInfo, runtime);\n\n const result: Media = {\n id: videoUuid,\n url: url,\n title: videoInfo.title,\n source: videoInfo.channel,\n description: videoInfo.description,\n text: transcript,\n };\n\n await runtime.setCache(cacheKey, result);\n\n return result;\n }\n\n private getVideoId(url: string): string {\n return stringToUuid(url);\n }\n\n async fetchVideoInfo(url: string): Promise<YtDlpJson> {\n if (url.endsWith(\".mp4\") || url.includes(\".mp4?\")) {\n try {\n const response = await fetch(url);\n if (response.ok) {\n // If the URL is a direct link to an MP4 file, return a simplified video info object\n return {\n title: path.basename(url),\n description: \"\",\n channel: \"\",\n };\n }\n } catch (error) {\n elizaLogger.log(\"Error downloading MP4 file:\", loggableError(error));\n // Fall back to using youtube-dl if direct download fails\n }\n }\n\n try {\n const result = await this.binaries.runYtDlp(url, {\n dumpJson: true,\n verbose: true,\n callHome: false,\n noCheckCertificates: true,\n preferFreeFormats: true,\n youtubeSkipDashManifest: true,\n writeSub: true,\n writeAutoSub: true,\n subLang: \"en\",\n skipDownload: true,\n });\n return result as YtDlpJson;\n } catch (error) {\n elizaLogger.log(\"Error fetching video info:\", loggableError(error));\n throw new Error(\"Failed to fetch video information\");\n }\n }\n\n private async getTranscript(\n url: string,\n videoInfo: YtDlpJson,\n runtime: IAgentRuntime,\n ): Promise<string> {\n elizaLogger.log(\"Getting transcript\");\n try {\n // Check for manual subtitles\n if (videoInfo.subtitles?.en) {\n elizaLogger.log(\"Manual subtitles found\");\n const srtContent = await this.downloadSRT(\n videoInfo.subtitles.en[0].url,\n );\n return this.parseSRT(srtContent);\n }\n\n // Check for automatic captions\n if (videoInfo.automatic_captions?.en) {\n elizaLogger.log(\"Automatic captions found\");\n const captionUrl = videoInfo.automatic_captions.en[0].url;\n const captionContent = await this.downloadCaption(captionUrl);\n return this.parseCaption(captionContent);\n }\n\n // Check if it's a music video\n if (videoInfo.categories?.includes(\"Music\")) {\n elizaLogger.log(\"Music video detected, no lyrics available\");\n return \"No lyrics available.\";\n }\n\n // Fall back to audio transcription\n elizaLogger.log(\n \"No subtitles or captions found, falling back to audio transcription\",\n );\n return this.transcribeAudio(url, runtime);\n } catch (error) {\n elizaLogger.log(\"Error in getTranscript:\", loggableError(error));\n throw error;\n }\n }\n\n private async downloadCaption(url: string): Promise<string> {\n elizaLogger.log(\"Downloading caption from:\", url);\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to download caption: ${response.statusText}`);\n }\n return await response.text();\n }\n\n private parseCaption(captionContent: string): string {\n elizaLogger.log(\"Parsing caption\");\n try {\n const jsonContent = JSON.parse(captionContent) as CaptionJson;\n if (Array.isArray(jsonContent.events)) {\n return jsonContent.events\n .filter((event): event is { segs: CaptionSegment[] } =>\n Array.isArray(event.segs),\n )\n .map((event) => event.segs.map((seg) => seg.utf8 ?? \"\").join(\"\"))\n .join(\"\")\n .replace(\"\\n\", \" \");\n } else {\n elizaLogger.log(\n \"Unexpected caption format:\",\n JSON.stringify(jsonContent),\n );\n return \"Error: Unable to parse captions\";\n }\n } catch (error) {\n elizaLogger.log(\"Error parsing caption:\", loggableError(error));\n return \"Error: Unable to parse captions\";\n }\n }\n\n private parseSRT(srtContent: string): string {\n // Simple SRT parser (replace with a more robust solution if needed)\n return srtContent\n .split(\"\\n\\n\")\n .map((block) => block.split(\"\\n\").slice(2).join(\" \"))\n .join(\" \");\n }\n\n private async downloadSRT(url: string): Promise<string> {\n elizaLogger.log(\"downloadSRT\");\n const response = await fetch(url);\n return await response.text();\n }\n\n async transcribeAudio(url: string, runtime: IAgentRuntime): Promise<string> {\n elizaLogger.log(\"Preparing audio for transcription...\");\n const mp4FilePath = path.join(this.dataDir, `${this.getVideoId(url)}.mp4`);\n\n const mp3FilePath = path.join(this.dataDir, `${this.getVideoId(url)}.mp3`);\n\n if (!fs.existsSync(mp3FilePath)) {\n if (fs.existsSync(mp4FilePath)) {\n elizaLogger.log(\"MP4 file found. Converting to MP3...\");\n await this.convertMp4ToMp3(mp4FilePath, mp3FilePath);\n } else {\n elizaLogger.log(\"Downloading audio...\");\n await this.downloadAudio(url, mp3FilePath);\n }\n }\n\n elizaLogger.log(`Audio prepared at ${mp3FilePath}`);\n\n const audioBuffer = fs.readFileSync(mp3FilePath);\n elizaLogger.log(`Audio file size: ${audioBuffer.length} bytes`);\n\n elizaLogger.log(\"Starting transcription...\");\n const startTime = Date.now();\n const transcriptionService = runtime.getService<ITranscriptionService>(\n ServiceType.TRANSCRIPTION,\n );\n\n if (!transcriptionService) {\n throw new Error(\"Transcription service not found\");\n }\n\n const result = await transcriptionService.transcribeAudio(audioBuffer);\n const transcript = result.text;\n\n const endTime = Date.now();\n elizaLogger.log(\n `Transcription completed in ${(endTime - startTime) / 1000} seconds`,\n );\n\n // Keep the MP3 in the content cache; callers own cache retention.\n return transcript || \"Transcription failed\";\n }\n\n private async convertMp4ToMp3(\n inputPath: string,\n outputPath: string,\n ): Promise<void> {\n await this.configureFfmpeg();\n return new Promise((resolve, reject) => {\n ffmpeg(inputPath)\n .output(outputPath)\n .noVideo()\n .audioCodec(\"libmp3lame\")\n .on(\"end\", () => {\n elizaLogger.log(\"Conversion to MP3 complete\");\n resolve();\n })\n .on(\"error\", (err) => {\n elizaLogger.log(\"Error converting to MP3:\", loggableError(err));\n reject(err);\n })\n .run();\n });\n }\n\n private async downloadAudio(\n url: string,\n outputFile: string,\n ): Promise<string> {\n elizaLogger.log(\"Downloading audio\");\n\n try {\n if (url.endsWith(\".mp4\") || url.includes(\".mp4?\")) {\n elizaLogger.log(\n \"Direct MP4 file detected, downloading and converting to MP3\",\n );\n const tempMp4File = path.join(tmpdir(), `${this.getVideoId(url)}.mp4`);\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(\n `Failed to download MP4: ${response.status} ${response.statusText}`,\n );\n }\n const arrayBuffer = await response.arrayBuffer();\n const buffer = Buffer.from(arrayBuffer);\n fs.writeFileSync(tempMp4File, buffer);\n\n await this.configureFfmpeg();\n await new Promise<void>((resolve, reject) => {\n ffmpeg(tempMp4File)\n .output(outputFile)\n .noVideo()\n .audioCodec(\"libmp3lame\")\n .on(\"end\", () => {\n fs.unlinkSync(tempMp4File);\n resolve();\n })\n .on(\"error\", (err) => {\n reject(err);\n })\n .run();\n });\n } else {\n elizaLogger.log(\n \"YouTube video detected, downloading audio with youtube-dl\",\n );\n await this.binaries.runYtDlp(url, {\n verbose: true,\n extractAudio: true,\n audioFormat: \"mp3\",\n output: outputFile,\n writeInfoJson: true,\n });\n }\n return outputFile;\n } catch (error) {\n elizaLogger.log(\"Error downloading audio:\", loggableError(error));\n throw new Error(\"Failed to download audio\");\n }\n }\n}\n",
|
|
6
|
+
"import { createHash } from \"node:crypto\";\nimport {\n createWriteStream,\n constants as fsConstants,\n promises as fsp,\n} from \"node:fs\";\nimport path from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { elizaLogger, resolveStateDir } from \"@elizaos/core\";\nimport youtubeDl from \"youtube-dl-exec\";\n\nexport type YtDlpRunner = (\n url: string,\n flags?: Record<string, string | boolean | number | undefined>,\n opts?: object,\n) => Promise<unknown>;\n\ninterface YtDlpFactory {\n create: (binaryPath: string) => YtDlpRunner;\n}\n\ntype YtDlpModule = YtDlpRunner & YtDlpFactory;\n\nfunction isYtDlpModule(value: unknown): value is YtDlpModule {\n return (\n typeof value === \"function\" &&\n typeof (value as { create?: unknown }).create === \"function\"\n );\n}\n\nif (!isYtDlpModule(youtubeDl)) {\n throw new TypeError(\"youtube-dl-exec did not expose the expected runner API\");\n}\n\nconst ytDlpModule = youtubeDl;\n\nconst YT_DLP_RELEASE_URL =\n \"https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest\";\nconst YT_DLP_UPDATE_THROTTLE_MS = 60 * 60 * 1000;\nconst YT_DLP_META_FILENAME = \"yt-dlp.meta.json\";\n\ninterface GitHubAsset {\n name: string;\n browser_download_url: string;\n size: number;\n}\n\ninterface GitHubRelease {\n tag_name: string;\n name?: string;\n assets: GitHubAsset[];\n}\n\ninterface YtDlpMeta {\n version: string;\n sha256: string;\n assetName: string;\n downloadedAt: number;\n lastUpdateAttemptedAt: number;\n}\n\ntype YtDlpSource = \"env\" | \"path\" | \"cache\" | \"bundled\";\n\nconst EXTRACTOR_BROKEN_PATTERNS: readonly RegExp[] = [\n /Unable to extract/i,\n /Sign in to confirm/i,\n /nsig extraction failed/i,\n /n_param signature/i,\n /Failed to parse JSON/i,\n /HTTP Error 403/i,\n /This video is unavailable/i,\n /Got error: HTTP Error 429/i,\n];\n\nexport interface BinaryResolverOptions {\n binariesDir?: string;\n releaseUrl?: string;\n fetchImpl?: typeof fetch;\n now?: () => number;\n disableAutoUpdate?: boolean;\n preferSystemPath?: boolean;\n updateThrottleMs?: number;\n envOverridePath?: string | null;\n}\n\nexport class BinaryResolver {\n private static _instance: BinaryResolver | null = null;\n\n static instance(): BinaryResolver {\n if (!BinaryResolver._instance)\n BinaryResolver._instance = new BinaryResolver();\n return BinaryResolver._instance;\n }\n\n /** Test hook: drop the singleton so the next instance() returns fresh state. */\n static resetForTests(): void {\n BinaryResolver._instance = null;\n }\n\n private readonly binariesDir: string;\n private readonly releaseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly now: () => number;\n private readonly disableAutoUpdate: boolean;\n private readonly preferSystemPath: boolean;\n private readonly updateThrottleMs: number;\n private readonly envOverridePath: string | null;\n\n private resolvedYtDlpPath: string | null = null;\n private resolvedYtDlpSource: YtDlpSource | null = null;\n private cachedRunner: YtDlpRunner | null = null;\n private resolvedFfmpegPath: string | null | undefined = undefined;\n private updateInFlight: Promise<void> | null = null;\n\n constructor(opts: BinaryResolverOptions = {}) {\n this.binariesDir = opts.binariesDir ?? defaultBinariesDir();\n this.releaseUrl = opts.releaseUrl ?? YT_DLP_RELEASE_URL;\n this.fetchImpl =\n opts.fetchImpl ??\n ((input: RequestInfo | URL, init?: RequestInit) =>\n globalThis.fetch(input, init));\n this.now = opts.now ?? Date.now;\n this.disableAutoUpdate =\n opts.disableAutoUpdate ?? envBool(\"ELIZA_DISABLE_YTDLP_AUTOUPDATE\");\n this.preferSystemPath =\n opts.preferSystemPath ?? envBool(\"ELIZA_YT_DLP_PREFER_PATH\");\n this.updateThrottleMs = opts.updateThrottleMs ?? YT_DLP_UPDATE_THROTTLE_MS;\n this.envOverridePath =\n opts.envOverridePath !== undefined\n ? opts.envOverridePath\n : (process.env.ELIZA_YT_DLP_PATH ?? null);\n }\n\n get cacheDir(): string {\n return this.binariesDir;\n }\n\n get cachedYtDlpPath(): string {\n return path.join(this.binariesDir, ytDlpFileName());\n }\n\n get metaPath(): string {\n return path.join(this.binariesDir, YT_DLP_META_FILENAME);\n }\n\n /**\n * Resolve the yt-dlp binary path. Order:\n * 1. ELIZA_YT_DLP_PATH env override.\n * 2. If ELIZA_YT_DLP_PREFER_PATH=1: system PATH, then cache.\n * 3. Otherwise: cache (download if missing) → PATH fallback.\n */\n async getYtDlpPath(): Promise<string> {\n if (this.resolvedYtDlpPath) return this.resolvedYtDlpPath;\n\n if (this.envOverridePath) {\n if (await isExecutable(this.envOverridePath)) {\n this.resolvedYtDlpPath = this.envOverridePath;\n this.resolvedYtDlpSource = \"env\";\n elizaLogger.log(\n `[plugin-video] Using yt-dlp from env override: ${this.envOverridePath}`,\n );\n return this.envOverridePath;\n }\n elizaLogger.warn(\n `[plugin-video] ELIZA_YT_DLP_PATH=${this.envOverridePath} is not executable; falling through.`,\n );\n }\n\n if (this.preferSystemPath) {\n const sys = await lookupOnPath(\"yt-dlp\");\n if (sys) {\n this.resolvedYtDlpPath = sys;\n this.resolvedYtDlpSource = \"path\";\n elizaLogger.log(`[plugin-video] Using yt-dlp from PATH: ${sys}`);\n return sys;\n }\n }\n\n const cachePath = this.cachedYtDlpPath;\n if (await isExecutable(cachePath)) {\n this.resolvedYtDlpPath = cachePath;\n this.resolvedYtDlpSource = \"cache\";\n elizaLogger.log(\n `[plugin-video] Using yt-dlp from managed cache: ${cachePath}`,\n );\n return cachePath;\n }\n\n const sys = await lookupOnPath(\"yt-dlp\");\n if (sys) {\n this.resolvedYtDlpPath = sys;\n this.resolvedYtDlpSource = \"path\";\n elizaLogger.log(\n `[plugin-video] Using yt-dlp from PATH (cache empty): ${sys}`,\n );\n return sys;\n }\n\n const bundled = await getBundledYtDlpPath();\n if (bundled) {\n this.resolvedYtDlpPath = bundled;\n this.resolvedYtDlpSource = \"bundled\";\n elizaLogger.log(\n `[plugin-video] Using yt-dlp from youtube-dl-exec bundle: ${bundled}`,\n );\n return bundled;\n }\n\n elizaLogger.log(\n \"[plugin-video] No yt-dlp binary found; downloading to managed cache.\",\n );\n await this.downloadYtDlp();\n this.resolvedYtDlpPath = cachePath;\n this.resolvedYtDlpSource = \"cache\";\n return cachePath;\n }\n\n /** Resolution order: ELIZA_FFMPEG_PATH env → system ffmpeg → ffmpeg-static. */\n async getFfmpegPath(): Promise<string | null> {\n if (this.resolvedFfmpegPath !== undefined) return this.resolvedFfmpegPath;\n\n const envPath = process.env.ELIZA_FFMPEG_PATH;\n if (envPath && (await isExecutable(envPath))) {\n this.resolvedFfmpegPath = envPath;\n return envPath;\n }\n\n const sys = await lookupOnPath(\"ffmpeg\");\n if (sys) {\n this.resolvedFfmpegPath = sys;\n return sys;\n }\n\n try {\n const mod: unknown = await import(\"ffmpeg-static\");\n const staticPath =\n typeof mod === \"string\"\n ? mod\n : mod && typeof mod === \"object\" && \"default\" in mod\n ? (mod.default as string | null | undefined)\n : null;\n if (\n typeof staticPath === \"string\" &&\n staticPath.length > 0 &&\n (await isExecutable(staticPath))\n ) {\n this.resolvedFfmpegPath = staticPath;\n return staticPath;\n }\n } catch (err) {\n elizaLogger.warn(\n `[plugin-video] ffmpeg-static not loadable: ${describeError(err)}`,\n );\n }\n\n this.resolvedFfmpegPath = null;\n return null;\n }\n\n /**\n * Build (or reuse) the yt-dlp runner bound to the resolved binary path.\n * The runner mirrors `youtube-dl-exec`'s default callable signature.\n */\n async getYtDlpRunner(): Promise<YtDlpRunner> {\n if (this.cachedRunner) return this.cachedRunner;\n const binPath = await this.getYtDlpPath();\n this.cachedRunner = ytDlpModule.create(binPath);\n return this.cachedRunner;\n }\n\n /**\n * Run yt-dlp with one auto-update + retry attempt on extractor-failure\n * patterns, when the active binary is the managed cache.\n */\n async runYtDlp(\n url: string,\n flags: Record<string, string | boolean | number | undefined>,\n ): Promise<unknown> {\n const runner = await this.getYtDlpRunner();\n try {\n return await runner(url, flags);\n } catch (err) {\n if (!this.shouldRetryWithUpdate(err)) {\n throw err;\n }\n const updated = await this.tryUpdate();\n if (!updated) {\n throw err;\n }\n const refreshed = await this.getYtDlpRunner();\n return await refreshed(url, flags);\n }\n }\n\n private shouldRetryWithUpdate(err: unknown): boolean {\n if (this.disableAutoUpdate) return false;\n // Auto-update fires only when we own the binary lifecycle (managed cache\n // or the bundled-with-youtube-dl-exec copy). User-managed binaries (env\n // override, system PATH) are out of scope — we don't touch homebrew.\n if (\n this.resolvedYtDlpSource !== \"cache\" &&\n this.resolvedYtDlpSource !== \"bundled\"\n ) {\n return false;\n }\n const msg = errorMessage(err);\n return EXTRACTOR_BROKEN_PATTERNS.some((p) => p.test(msg));\n }\n\n /**\n * Run a yt-dlp update attempt, throttled to once per `updateThrottleMs`.\n * Returns true iff a fresh binary was successfully installed.\n */\n private async tryUpdate(): Promise<boolean> {\n if (this.updateInFlight) {\n await this.updateInFlight;\n return true;\n }\n\n const meta = await this.readMeta();\n // No metadata yet means we have never attempted an update; first failure\n // should always be allowed to try.\n if (meta) {\n const sinceLast = this.now() - meta.lastUpdateAttemptedAt;\n if (sinceLast < this.updateThrottleMs) {\n elizaLogger.warn(\n `[plugin-video] yt-dlp update throttled (last attempt ${Math.floor(sinceLast / 1000)}s ago).`,\n );\n return false;\n }\n }\n\n const job = (async () => {\n await this.touchUpdateAttempt(meta);\n await this.downloadYtDlp();\n this.resetRunnerCache();\n })();\n this.updateInFlight = job;\n try {\n await job;\n return true;\n } catch (err) {\n elizaLogger.error(\n `[plugin-video] yt-dlp update failed: ${describeError(err)}`,\n );\n return false;\n } finally {\n this.updateInFlight = null;\n }\n }\n\n private resetRunnerCache(): void {\n this.cachedRunner = null;\n this.resolvedYtDlpPath = null;\n this.resolvedYtDlpSource = null;\n }\n\n /** Force a fresh yt-dlp download regardless of throttle. Test/admin hook. */\n async forceUpdateYtDlp(): Promise<{ version: string; path: string }> {\n const meta = await this.downloadYtDlp();\n this.resetRunnerCache();\n await this.getYtDlpPath();\n return { version: meta.version, path: this.cachedYtDlpPath };\n }\n\n private async readMeta(): Promise<YtDlpMeta | null> {\n try {\n const raw = await fsp.readFile(this.metaPath, \"utf8\");\n return JSON.parse(raw) as YtDlpMeta;\n } catch {\n return null;\n }\n }\n\n private async writeMeta(meta: YtDlpMeta): Promise<void> {\n await fsp.mkdir(this.binariesDir, { recursive: true });\n await fsp.writeFile(this.metaPath, JSON.stringify(meta, null, 2));\n }\n\n private async touchUpdateAttempt(prev: YtDlpMeta | null): Promise<void> {\n const next: YtDlpMeta = prev\n ? { ...prev, lastUpdateAttemptedAt: this.now() }\n : {\n version: \"\",\n sha256: \"\",\n assetName: \"\",\n downloadedAt: 0,\n lastUpdateAttemptedAt: this.now(),\n };\n await this.writeMeta(next);\n }\n\n /**\n * Download the latest yt-dlp release for this platform, verify the SHA256\n * against the published `SHA2-256SUMS`, and atomically replace the cached\n * binary. Returns the new metadata on success; throws on any failure.\n */\n async downloadYtDlp(): Promise<YtDlpMeta> {\n const release = await this.fetchRelease();\n const assetName = ytDlpAssetName();\n const asset = release.assets.find((a) => a.name === assetName);\n if (!asset) {\n throw new Error(\n `yt-dlp release ${release.tag_name} has no asset named '${assetName}'`,\n );\n }\n const sumsAsset = release.assets.find((a) => a.name === \"SHA2-256SUMS\");\n if (!sumsAsset) {\n throw new Error(\n `yt-dlp release ${release.tag_name} has no SHA2-256SUMS asset`,\n );\n }\n\n const expectedSha = await this.fetchExpectedSha(\n sumsAsset.browser_download_url,\n assetName,\n );\n\n await fsp.mkdir(this.binariesDir, { recursive: true });\n const tmpPath = `${this.cachedYtDlpPath}.tmp.${process.pid}.${Date.now()}`;\n await this.downloadToFile(asset.browser_download_url, tmpPath);\n\n const actualSha = await sha256OfFile(tmpPath);\n if (actualSha !== expectedSha) {\n await safeUnlink(tmpPath);\n throw new Error(\n `yt-dlp SHA256 mismatch: expected ${expectedSha}, got ${actualSha}`,\n );\n }\n\n await fsp.chmod(tmpPath, 0o755);\n await fsp.rename(tmpPath, this.cachedYtDlpPath);\n\n const meta: YtDlpMeta = {\n version: release.tag_name,\n sha256: expectedSha,\n assetName,\n downloadedAt: this.now(),\n lastUpdateAttemptedAt: this.now(),\n };\n await this.writeMeta(meta);\n elizaLogger.log(\n `[plugin-video] yt-dlp ${release.tag_name} installed at ${this.cachedYtDlpPath}`,\n );\n return meta;\n }\n\n private async fetchRelease(): Promise<GitHubRelease> {\n const res = await this.fetchImpl(this.releaseUrl, {\n headers: { Accept: \"application/vnd.github+json\" },\n });\n if (!res.ok) {\n throw new Error(\n `yt-dlp release fetch failed: ${res.status} ${res.statusText}`,\n );\n }\n return (await res.json()) as GitHubRelease;\n }\n\n private async fetchExpectedSha(\n sumsUrl: string,\n assetName: string,\n ): Promise<string> {\n const res = await this.fetchImpl(sumsUrl);\n if (!res.ok) {\n throw new Error(\n `SHA2-256SUMS fetch failed: ${res.status} ${res.statusText}`,\n );\n }\n const text = await res.text();\n for (const line of text.split(/\\r?\\n/)) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n const parts = trimmed.split(/\\s+/);\n if (parts.length >= 2 && parts[parts.length - 1] === assetName) {\n return parts[0].toLowerCase();\n }\n }\n throw new Error(`SHA2-256SUMS missing entry for ${assetName}`);\n }\n\n private async downloadToFile(url: string, dest: string): Promise<void> {\n const res = await this.fetchImpl(url);\n if (!res.ok || !res.body) {\n throw new Error(\n `yt-dlp binary fetch failed: ${res.status} ${res.statusText}`,\n );\n }\n const nodeStream = Readable.fromWeb(\n res.body as Parameters<typeof Readable.fromWeb>[0],\n );\n await pipeline(nodeStream, createWriteStream(dest));\n }\n}\n\nfunction defaultBinariesDir(): string {\n const explicit = process.env.ELIZA_BINARIES_DIR;\n if (explicit) return explicit;\n return path.join(resolveStateDir(), \"binaries\");\n}\n\nexport function ytDlpAssetName(): string {\n const platform = process.platform;\n const arch = process.arch;\n if (platform === \"darwin\") return \"yt-dlp_macos\";\n if (platform === \"win32\")\n return arch === \"ia32\" ? \"yt-dlp_x86.exe\" : \"yt-dlp.exe\";\n if (platform === \"linux\") {\n if (arch === \"arm64\") return \"yt-dlp_linux_aarch64\";\n if (arch === \"arm\") return \"yt-dlp_linux_armv7l\";\n return \"yt-dlp_linux\";\n }\n throw new Error(`Unsupported platform for yt-dlp: ${platform}/${arch}`);\n}\n\nexport function ytDlpFileName(): string {\n return process.platform === \"win32\" ? \"yt-dlp.exe\" : \"yt-dlp\";\n}\n\nasync function isExecutable(p: string): Promise<boolean> {\n try {\n await fsp.access(p, fsConstants.X_OK);\n const st = await fsp.stat(p);\n return st.isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Look for the yt-dlp binary that `youtube-dl-exec`'s postinstall ships into\n * its own `bin/` directory. Tries the package's internal constants first\n * (which the wrapper uses by default), then falls back to a hardcoded\n * relative path. Returns the path only if the binary is actually executable.\n */\nasync function getBundledYtDlpPath(): Promise<string | null> {\n try {\n const constantsModule = (await import(\n \"youtube-dl-exec/src/constants.js\" as string\n )) as { default?: { YOUTUBE_DL_PATH?: string } } & {\n YOUTUBE_DL_PATH?: string;\n };\n const fromConstants =\n constantsModule.default?.YOUTUBE_DL_PATH ??\n constantsModule.YOUTUBE_DL_PATH;\n if (\n typeof fromConstants === \"string\" &&\n fromConstants.length > 0 &&\n (await isExecutable(fromConstants))\n ) {\n return fromConstants;\n }\n } catch {\n /* package internals unavailable; fall through to relative resolve */\n }\n try {\n const { createRequire } = await import(\"node:module\");\n const req = createRequire(import.meta.url);\n const pkgPath = req.resolve(\"youtube-dl-exec/package.json\");\n const candidate = path.join(path.dirname(pkgPath), \"bin\", ytDlpFileName());\n if (await isExecutable(candidate)) return candidate;\n } catch {\n /* youtube-dl-exec not installed in this tree */\n }\n return null;\n}\n\nasync function lookupOnPath(name: string): Promise<string | null> {\n const pathEnv = process.env.PATH;\n if (!pathEnv) return null;\n const exts =\n process.platform === \"win32\"\n ? (process.env.PATHEXT ?? \".EXE;.CMD;.BAT\")\n .split(\";\")\n .map((e) => e.toLowerCase())\n : [\"\"];\n for (const dir of pathEnv.split(path.delimiter)) {\n if (!dir) continue;\n for (const ext of exts) {\n const candidate = path.join(dir, `${name}${ext}`);\n if (await isExecutable(candidate)) {\n return candidate;\n }\n }\n }\n return null;\n}\n\nasync function sha256OfFile(p: string): Promise<string> {\n const hash = createHash(\"sha256\");\n const fd = await fsp.open(p, \"r\");\n try {\n const stream = fd.createReadStream();\n for await (const chunk of stream) hash.update(chunk as Buffer);\n } finally {\n await fd.close();\n }\n return hash.digest(\"hex\");\n}\n\nasync function safeUnlink(p: string): Promise<void> {\n try {\n await fsp.unlink(p);\n } catch {\n /* swallow */\n }\n}\n\nfunction envBool(name: string): boolean {\n const v = process.env[name];\n if (!v) return false;\n return v === \"1\" || v.toLowerCase() === \"true\";\n}\n\nfunction errorMessage(err: unknown): string {\n if (!err) return \"\";\n if (typeof err === \"string\") return err;\n const anyErr = err as { stderr?: unknown; message?: unknown };\n if (typeof anyErr.stderr === \"string\" && anyErr.stderr.length > 0)\n return anyErr.stderr;\n if (typeof anyErr.message === \"string\") return anyErr.message;\n try {\n return JSON.stringify(err);\n } catch {\n return String(err);\n }\n}\n\nfunction describeError(err: unknown): string {\n return errorMessage(err) || \"(no message)\";\n}\n",
|
|
7
|
+
"import type { Plugin } from \"@elizaos/core\";\nimport { VideoService } from \"./services/video\";\n\nconst videoPlugin: Plugin = {\n name: \"video\",\n description: \"Video processing and transcription capabilities\",\n services: [VideoService],\n actions: [],\n providers: [],\n routes: [],\n async dispose(runtime) {\n const svc = runtime.getService<VideoService>(VideoService.serviceType);\n await svc?.stop();\n },\n};\n\nexport default videoPlugin;\n"
|
|
8
|
+
],
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "17A8C3F43B7ECE4364756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export type YtDlpRunner = (url: string, flags?: Record<string, string | boolean | number | undefined>, opts?: object) => Promise<unknown>;
|
|
2
|
+
interface YtDlpMeta {
|
|
3
|
+
version: string;
|
|
4
|
+
sha256: string;
|
|
5
|
+
assetName: string;
|
|
6
|
+
downloadedAt: number;
|
|
7
|
+
lastUpdateAttemptedAt: number;
|
|
8
|
+
}
|
|
9
|
+
export interface BinaryResolverOptions {
|
|
10
|
+
binariesDir?: string;
|
|
11
|
+
releaseUrl?: string;
|
|
12
|
+
fetchImpl?: typeof fetch;
|
|
13
|
+
now?: () => number;
|
|
14
|
+
disableAutoUpdate?: boolean;
|
|
15
|
+
preferSystemPath?: boolean;
|
|
16
|
+
updateThrottleMs?: number;
|
|
17
|
+
envOverridePath?: string | null;
|
|
18
|
+
}
|
|
19
|
+
export declare class BinaryResolver {
|
|
20
|
+
private static _instance;
|
|
21
|
+
static instance(): BinaryResolver;
|
|
22
|
+
/** Test hook: drop the singleton so the next instance() returns fresh state. */
|
|
23
|
+
static resetForTests(): void;
|
|
24
|
+
private readonly binariesDir;
|
|
25
|
+
private readonly releaseUrl;
|
|
26
|
+
private readonly fetchImpl;
|
|
27
|
+
private readonly now;
|
|
28
|
+
private readonly disableAutoUpdate;
|
|
29
|
+
private readonly preferSystemPath;
|
|
30
|
+
private readonly updateThrottleMs;
|
|
31
|
+
private readonly envOverridePath;
|
|
32
|
+
private resolvedYtDlpPath;
|
|
33
|
+
private resolvedYtDlpSource;
|
|
34
|
+
private cachedRunner;
|
|
35
|
+
private resolvedFfmpegPath;
|
|
36
|
+
private updateInFlight;
|
|
37
|
+
constructor(opts?: BinaryResolverOptions);
|
|
38
|
+
get cacheDir(): string;
|
|
39
|
+
get cachedYtDlpPath(): string;
|
|
40
|
+
get metaPath(): string;
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the yt-dlp binary path. Order:
|
|
43
|
+
* 1. ELIZA_YT_DLP_PATH env override.
|
|
44
|
+
* 2. If ELIZA_YT_DLP_PREFER_PATH=1: system PATH, then cache.
|
|
45
|
+
* 3. Otherwise: cache (download if missing) → PATH fallback.
|
|
46
|
+
*/
|
|
47
|
+
getYtDlpPath(): Promise<string>;
|
|
48
|
+
/** Resolution order: ELIZA_FFMPEG_PATH env → system ffmpeg → ffmpeg-static. */
|
|
49
|
+
getFfmpegPath(): Promise<string | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Build (or reuse) the yt-dlp runner bound to the resolved binary path.
|
|
52
|
+
* The runner mirrors `youtube-dl-exec`'s default callable signature.
|
|
53
|
+
*/
|
|
54
|
+
getYtDlpRunner(): Promise<YtDlpRunner>;
|
|
55
|
+
/**
|
|
56
|
+
* Run yt-dlp with one auto-update + retry attempt on extractor-failure
|
|
57
|
+
* patterns, when the active binary is the managed cache.
|
|
58
|
+
*/
|
|
59
|
+
runYtDlp(url: string, flags: Record<string, string | boolean | number | undefined>): Promise<unknown>;
|
|
60
|
+
private shouldRetryWithUpdate;
|
|
61
|
+
/**
|
|
62
|
+
* Run a yt-dlp update attempt, throttled to once per `updateThrottleMs`.
|
|
63
|
+
* Returns true iff a fresh binary was successfully installed.
|
|
64
|
+
*/
|
|
65
|
+
private tryUpdate;
|
|
66
|
+
private resetRunnerCache;
|
|
67
|
+
/** Force a fresh yt-dlp download regardless of throttle. Test/admin hook. */
|
|
68
|
+
forceUpdateYtDlp(): Promise<{
|
|
69
|
+
version: string;
|
|
70
|
+
path: string;
|
|
71
|
+
}>;
|
|
72
|
+
private readMeta;
|
|
73
|
+
private writeMeta;
|
|
74
|
+
private touchUpdateAttempt;
|
|
75
|
+
/**
|
|
76
|
+
* Download the latest yt-dlp release for this platform, verify the SHA256
|
|
77
|
+
* against the published `SHA2-256SUMS`, and atomically replace the cached
|
|
78
|
+
* binary. Returns the new metadata on success; throws on any failure.
|
|
79
|
+
*/
|
|
80
|
+
downloadYtDlp(): Promise<YtDlpMeta>;
|
|
81
|
+
private fetchRelease;
|
|
82
|
+
private fetchExpectedSha;
|
|
83
|
+
private downloadToFile;
|
|
84
|
+
}
|
|
85
|
+
export declare function ytDlpAssetName(): string;
|
|
86
|
+
export declare function ytDlpFileName(): string;
|
|
87
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type IAgentRuntime, IVideoService, type Media, type Service, type VideoDownloadOptions, type VideoFormat, type VideoInfo, type VideoProcessingOptions } from "@elizaos/core";
|
|
2
|
+
import { BinaryResolver } from "./binaries";
|
|
3
|
+
/** Minimal yt-dlp JSON shape used by this service (fields vary by extractor). */
|
|
4
|
+
interface YtDlpSubtitleTrack {
|
|
5
|
+
url: string;
|
|
6
|
+
}
|
|
7
|
+
interface YtDlpJson {
|
|
8
|
+
title?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
channel?: string;
|
|
11
|
+
duration?: number;
|
|
12
|
+
thumbnail?: string;
|
|
13
|
+
view_count?: number;
|
|
14
|
+
upload_date?: string;
|
|
15
|
+
formats?: YtDlpFormatRow[];
|
|
16
|
+
categories?: string[];
|
|
17
|
+
subtitles?: Record<string, YtDlpSubtitleTrack[]>;
|
|
18
|
+
automatic_captions?: Record<string, YtDlpSubtitleTrack[]>;
|
|
19
|
+
}
|
|
20
|
+
interface YtDlpFormatRow {
|
|
21
|
+
format_id?: string;
|
|
22
|
+
url?: string;
|
|
23
|
+
ext?: string;
|
|
24
|
+
quality?: string | number;
|
|
25
|
+
filesize?: number;
|
|
26
|
+
vcodec?: string;
|
|
27
|
+
acodec?: string;
|
|
28
|
+
resolution?: string;
|
|
29
|
+
fps?: number;
|
|
30
|
+
tbr?: number;
|
|
31
|
+
}
|
|
32
|
+
export declare class VideoService extends IVideoService {
|
|
33
|
+
readonly capabilityDescription = "Video download, processing, and conversion capabilities";
|
|
34
|
+
static readonly serviceType: "video";
|
|
35
|
+
private cacheKey;
|
|
36
|
+
private dataDir;
|
|
37
|
+
private readonly binaries;
|
|
38
|
+
private ffmpegPathConfigured;
|
|
39
|
+
/** Serialize downloads/processing so cache keys and temp files do not race. */
|
|
40
|
+
private processingChain;
|
|
41
|
+
constructor(runtime?: IAgentRuntime, binaries?: BinaryResolver);
|
|
42
|
+
static start(runtime: IAgentRuntime): Promise<Service>;
|
|
43
|
+
initialize(_runtime: IAgentRuntime): Promise<void>;
|
|
44
|
+
private configureFfmpeg;
|
|
45
|
+
stop(): Promise<void>;
|
|
46
|
+
getVideoInfo(url: string): Promise<VideoInfo>;
|
|
47
|
+
downloadVideo(url: string, options?: VideoDownloadOptions): Promise<string>;
|
|
48
|
+
extractAudio(videoPath: string, outputPath?: string): Promise<string>;
|
|
49
|
+
getThumbnail(videoPath: string, timestamp?: number): Promise<string>;
|
|
50
|
+
convertVideo(videoPath: string, outputPath: string, options?: VideoProcessingOptions): Promise<string>;
|
|
51
|
+
getAvailableFormats(url: string): Promise<VideoFormat[]>;
|
|
52
|
+
private ensureDataDirectoryExists;
|
|
53
|
+
isVideoUrl(url: string): boolean;
|
|
54
|
+
downloadMedia(url: string): Promise<string>;
|
|
55
|
+
processVideo(url: string, runtime: IAgentRuntime): Promise<Media>;
|
|
56
|
+
private processVideoFromUrl;
|
|
57
|
+
private getVideoId;
|
|
58
|
+
fetchVideoInfo(url: string): Promise<YtDlpJson>;
|
|
59
|
+
private getTranscript;
|
|
60
|
+
private downloadCaption;
|
|
61
|
+
private parseCaption;
|
|
62
|
+
private parseSRT;
|
|
63
|
+
private downloadSRT;
|
|
64
|
+
transcribeAudio(url: string, runtime: IAgentRuntime): Promise<string>;
|
|
65
|
+
private convertMp4ToMp3;
|
|
66
|
+
private downloadAudio;
|
|
67
|
+
}
|
|
68
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elizaos/plugin-video",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3-beta.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -8,32 +8,48 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
|
+
"eliza-source": {
|
|
12
|
+
"types": "./src/index.ts",
|
|
13
|
+
"import": "./src/index.ts",
|
|
14
|
+
"default": "./src/index.ts"
|
|
15
|
+
},
|
|
11
16
|
"import": "./dist/index.js",
|
|
12
17
|
"default": "./dist/index.js"
|
|
13
18
|
},
|
|
14
|
-
"./package.json": "./package.json"
|
|
19
|
+
"./package.json": "./package.json",
|
|
20
|
+
"./*.css": "./dist/*.css",
|
|
21
|
+
"./*": {
|
|
22
|
+
"types": "./dist/*.d.ts",
|
|
23
|
+
"eliza-source": {
|
|
24
|
+
"types": "./src/*.ts",
|
|
25
|
+
"import": "./src/*.ts",
|
|
26
|
+
"default": "./src/*.ts"
|
|
27
|
+
},
|
|
28
|
+
"import": "./dist/*.js",
|
|
29
|
+
"default": "./dist/*.js"
|
|
30
|
+
}
|
|
15
31
|
},
|
|
16
32
|
"files": [
|
|
33
|
+
"registry-entry.json",
|
|
17
34
|
"dist",
|
|
18
35
|
"package.json",
|
|
19
|
-
"
|
|
36
|
+
"build.ts"
|
|
20
37
|
],
|
|
21
38
|
"dependencies": {
|
|
22
|
-
"@elizaos/core": "2.0.
|
|
39
|
+
"@elizaos/core": "2.0.3-beta.6",
|
|
23
40
|
"ffmpeg-static": "^5.3.0",
|
|
24
41
|
"fluent-ffmpeg": "2.1.3",
|
|
25
42
|
"youtube-dl-exec": "^3.1.5"
|
|
26
43
|
},
|
|
27
44
|
"devDependencies": {
|
|
28
45
|
"@types/node": "22.19.17",
|
|
29
|
-
"tsup": "^8.5.1",
|
|
30
46
|
"typescript": "^6.0.3",
|
|
31
47
|
"vitest": "^4.0.17"
|
|
32
48
|
},
|
|
33
49
|
"scripts": {
|
|
34
|
-
"build": "
|
|
35
|
-
"dev": "
|
|
36
|
-
"typecheck": "
|
|
50
|
+
"build": "bun run build.ts",
|
|
51
|
+
"dev": "bun run build.ts",
|
|
52
|
+
"typecheck": "tsgo --noEmit -p tsconfig.json",
|
|
37
53
|
"test": "vitest run --config vitest.config.ts"
|
|
38
54
|
},
|
|
39
55
|
"agentConfig": {
|
|
@@ -42,5 +58,6 @@
|
|
|
42
58
|
},
|
|
43
59
|
"publishConfig": {
|
|
44
60
|
"access": "public"
|
|
45
|
-
}
|
|
61
|
+
},
|
|
62
|
+
"gitHead": "990dc996172b3e0fb525a75052a5ac28a4cd4de5"
|
|
46
63
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "video",
|
|
3
|
+
"name": "Video",
|
|
4
|
+
"description": "Video download, audio extraction, and transcription helpers (YouTube, Vimeo, direct MP4).",
|
|
5
|
+
"npmName": "@elizaos/plugin-video",
|
|
6
|
+
"version": "0.1.9-alpha.1",
|
|
7
|
+
"source": "bundled",
|
|
8
|
+
"tags": ["video", "youtube", "transcription", "media"],
|
|
9
|
+
"config": {},
|
|
10
|
+
"render": {
|
|
11
|
+
"visible": true,
|
|
12
|
+
"pinTo": [],
|
|
13
|
+
"style": "card",
|
|
14
|
+
"icon": "Video",
|
|
15
|
+
"group": "media",
|
|
16
|
+
"groupOrder": 5,
|
|
17
|
+
"actions": ["enable", "configure"]
|
|
18
|
+
},
|
|
19
|
+
"resources": {},
|
|
20
|
+
"dependsOn": [],
|
|
21
|
+
"kind": "plugin",
|
|
22
|
+
"subtype": "media"
|
|
23
|
+
}
|
package/tsup.config.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from "tsup";
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
entry: ["src/index.ts"],
|
|
5
|
-
outDir: "dist",
|
|
6
|
-
sourcemap: true,
|
|
7
|
-
clean: true,
|
|
8
|
-
format: ["esm"], // Ensure you're targeting CommonJS
|
|
9
|
-
external: [
|
|
10
|
-
"dotenv", // Externalize dotenv to prevent bundling
|
|
11
|
-
"fs", // Externalize fs to use Node.js built-in module
|
|
12
|
-
"path", // Externalize other built-ins if necessary
|
|
13
|
-
"@reflink/reflink",
|
|
14
|
-
"@node-llama-cpp",
|
|
15
|
-
"https",
|
|
16
|
-
"http",
|
|
17
|
-
"agentkeepalive",
|
|
18
|
-
"zod",
|
|
19
|
-
"@elizaos/core",
|
|
20
|
-
// Add other modules you want to externalize
|
|
21
|
-
],
|
|
22
|
-
});
|