{"_id":"@dymoo/media-understanding","_rev":"6-1dfa656345027e422c61831f885997aa","name":"@dymoo/media-understanding","dist-tags":{"latest":"1.1.0"},"versions":{"0.1.0":{"name":"@dymoo/media-understanding","version":"0.1.0","keywords":["mcp","llm","media","video","audio","transcription","whisper","ffmpeg","opencode","agents"],"author":{"name":"dymoo"},"license":"MIT","_id":"@dymoo/media-understanding@0.1.0","maintainers":[{"name":"dymoo","email":"dyl000@protonmail.com"}],"homepage":"https://github.com/dymoo/media-understanding#readme","bugs":{"url":"https://github.com/dymoo/media-understanding/issues"},"bin":{"media-understanding":"dist/cli.js","media-understanding-mcp":"dist/mcp.js"},"dist":{"shasum":"42aec99468126785bd17e5f8e0a10b167f751e22","tarball":"https://registry.npmjs.org/@dymoo/media-understanding/-/media-understanding-0.1.0.tgz","fileCount":61,"integrity":"sha512-TZiOlv/2Xlf7qKdOQPC0eVul1a7j3lAY5yRRdLk8rxg0Aa/aC29tVE5iApgc/9tVngmavJhLp+Id8O/rEHH6sw==","signatures":[{"sig":"MEUCIDHHkDcaH5AaXGdEfYs7+j8ZEdNaxYqda1zUPraOvtwQAiEAy1ZnZM5g6dHNb+RVEOvV1+Bf8cwvRE72fA8F7pZB+bY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":373173},"type":"module","engines":{"node":">=22"},"exports":{".":"./dist/index.js"},"gitHead":"3b54ec15f5de7f41f485ac33643eb084bdb3889b","scripts":{"lint":"eslint src","test":"node --test dist/tests/*.test.js","build":"tsc","format":"prettier --write .","pretest":"tsc","check-all":"pnpm run format:check && pnpm run lint && pnpm run typecheck","typecheck":"tsc --noEmit","build:watch":"tsc --watch","postinstall":"node scripts/install-node-av.mjs && node scripts/download-whisper-model.mjs","format:check":"prettier --check .","prepublishOnly":"pnpm run check-all && pnpm run build"},"_npmUser":{"name":"dymoo","email":"dyl000@protonmail.com"},"repository":{"url":"git+https://github.com/dymoo/media-understanding.git","type":"git"},"_npmVersion":"11.6.1","description":"MCP server that converts audio/video/image to text + images for LLM consumption","directories":{},"_nodeVersion":"24.11.0","dependencies":{"zod":"^3.24.0","sharp":"^0.34.5","node-av":"^5.2.2","file-type":"^21.3.1","@modelcontextprotocol/sdk":"^1.27.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^10.0.3","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^22.0.0","@types/sharp":"^0.32.0","typescript-eslint":"^8.57.0","eslint-config-prettier":"^10.1.8"},"optionalDependencies":{"@seydx/node-av-linux-x64":"^5.2.2","@seydx/node-av-darwin-x64":"^5.2.2","@seydx/node-av-linux-arm64":"^5.2.2","@seydx/node-av-darwin-arm64":"^5.2.2","@seydx/node-av-win32-x64-msvc":"^5.2.2","@seydx/node-av-win32-x64-mingw":"^5.2.2","@seydx/node-av-win32-arm64-msvc":"^5.2.2","@seydx/node-av-win32-arm64-mingw":"^5.2.2"},"_npmOperationalInternal":{"tmp":"tmp/media-understanding_0.1.0_1773409319524_0.40401791216224825","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@dymoo/media-understanding","version":"0.1.1","keywords":["mcp","llm","media","video","audio","transcription","whisper","ffmpeg","opencode","agents"],"author":{"name":"dymoo"},"license":"MIT","_id":"@dymoo/media-understanding@0.1.1","maintainers":[{"name":"dymoo","email":"dyl000@protonmail.com"}],"homepage":"https://github.com/dymoo/media-understanding#readme","bugs":{"url":"https://github.com/dymoo/media-understanding/issues"},"bin":{"media-understanding":"dist/cli.js","media-understanding-mcp":"dist/mcp.js"},"dist":{"shasum":"dffc8ebba871843d9b5a622456b04e2b1c590e74","tarball":"https://registry.npmjs.org/@dymoo/media-understanding/-/media-understanding-0.1.1.tgz","fileCount":61,"integrity":"sha512-u8lD2fgR0hvm5ncmvA257M43NOwL7DsJtDUPti1nKAwReO/z83HGF+jUzury39u/zAOVo8OGWl+A1F5jn6ApKw==","signatures":[{"sig":"MEUCIQCy688ZJ09g1e50SwZ5LaOqDaBZJB+BOmW7dVo+tCVS0gIgFXRuHNqcQ7GR1KrrKNw94w4t5dw/6Tg7Z1rWlalrw38=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dymoo%2fmedia-understanding@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":373173},"type":"module","engines":{"node":">=22"},"exports":{".":"./dist/index.js"},"gitHead":"164093a83f20890ea2b45ad6ce3649cd499de02d","scripts":{"lint":"eslint src","test":"node --test dist/tests/*.test.js","build":"tsc","format":"prettier --write .","pretest":"tsc","check-all":"pnpm run format:check && pnpm run lint && pnpm run typecheck","typecheck":"tsc --noEmit","build:watch":"tsc --watch","postinstall":"node scripts/install-node-av.mjs && node scripts/download-whisper-model.mjs","format:check":"prettier --check .","prepublishOnly":"pnpm run check-all && pnpm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b15edc03-63fc-4fac-9ade-eed16c5d485d"}},"repository":{"url":"git+https://github.com/dymoo/media-understanding.git","type":"git"},"_npmVersion":"11.9.0","description":"MCP server that converts audio/video/image to text + images for LLM consumption","directories":{},"_nodeVersion":"24.14.0","dependencies":{"zod":"^3.24.0","sharp":"^0.34.5","node-av":"^5.2.2","file-type":"^21.3.1","@modelcontextprotocol/sdk":"^1.27.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^10.0.3","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^22.0.0","@types/sharp":"^0.32.0","typescript-eslint":"^8.57.0","eslint-config-prettier":"^10.1.8"},"optionalDependencies":{"@seydx/node-av-linux-x64":"^5.2.2","@seydx/node-av-darwin-x64":"^5.2.2","@seydx/node-av-linux-arm64":"^5.2.2","@seydx/node-av-darwin-arm64":"^5.2.2","@seydx/node-av-win32-x64-msvc":"^5.2.2","@seydx/node-av-win32-x64-mingw":"^5.2.2","@seydx/node-av-win32-arm64-msvc":"^5.2.2","@seydx/node-av-win32-arm64-mingw":"^5.2.2"},"_npmOperationalInternal":{"tmp":"tmp/media-understanding_0.1.1_1773409626385_0.8642056149141177","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@dymoo/media-understanding","version":"0.1.2","keywords":["mcp","llm","media","video","audio","transcription","whisper","ffmpeg","opencode","agents"],"author":{"name":"dymoo"},"license":"MIT","_id":"@dymoo/media-understanding@0.1.2","maintainers":[{"name":"dymoo","email":"dyl000@protonmail.com"}],"homepage":"https://github.com/dymoo/media-understanding#readme","bugs":{"url":"https://github.com/dymoo/media-understanding/issues"},"bin":{"media-understanding":"dist/cli.js","media-understanding-mcp":"dist/mcp.js"},"dist":{"shasum":"67a51ac4857676644d4928706a287964cbe07002","tarball":"https://registry.npmjs.org/@dymoo/media-understanding/-/media-understanding-0.1.2.tgz","fileCount":61,"integrity":"sha512-YZwipCiE4lm3Fh2hIIkSnSMBHTQ/DO0GpoQ3H+U+5Tz9iqoOKQh7xRH9hy6OZzCOzeON6lF9DQP+evHDwRCS4A==","signatures":[{"sig":"MEQCIGLZUktN9RTc4XzetwSl5hBEm0K+Q0EqRD41KOEsaHVpAiBiqnUnYwEuFC5pvAzqwb4Lme1ORO8z61ihYdDpcSY7RA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dymoo%2fmedia-understanding@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":374693},"type":"module","engines":{"node":">=22"},"exports":{".":"./dist/index.js"},"gitHead":"b39bb0e785e4d90aeb15aa317600f49fe87fa71b","scripts":{"lint":"eslint src","test":"node --test dist/tests/*.test.js","build":"tsc","format":"prettier --write .","pretest":"tsc","check-all":"pnpm run format:check && pnpm run lint && pnpm run typecheck","typecheck":"tsc --noEmit","build:watch":"tsc --watch","postinstall":"node scripts/install-node-av.mjs && node scripts/download-whisper-model.mjs","format:check":"prettier --check .","prepublishOnly":"pnpm run check-all && pnpm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b15edc03-63fc-4fac-9ade-eed16c5d485d"}},"repository":{"url":"git+https://github.com/dymoo/media-understanding.git","type":"git"},"_npmVersion":"11.9.0","description":"MCP server that converts audio/video/image to text + images for LLM consumption","directories":{},"_nodeVersion":"24.14.0","dependencies":{"zod":"^3.24.0","sharp":"^0.34.5","node-av":"^5.2.2","file-type":"^21.3.1","@modelcontextprotocol/sdk":"^1.27.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^10.0.3","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^22.0.0","@types/sharp":"^0.32.0","typescript-eslint":"^8.57.0","eslint-config-prettier":"^10.1.8"},"optionalDependencies":{"@seydx/node-av-linux-x64":"^5.2.2","@seydx/node-av-darwin-x64":"^5.2.2","@seydx/node-av-linux-arm64":"^5.2.2","@seydx/node-av-darwin-arm64":"^5.2.2","@seydx/node-av-win32-x64-msvc":"^5.2.2","@seydx/node-av-win32-x64-mingw":"^5.2.2","@seydx/node-av-win32-arm64-msvc":"^5.2.2","@seydx/node-av-win32-arm64-mingw":"^5.2.2"},"_npmOperationalInternal":{"tmp":"tmp/media-understanding_0.1.2_1773416552174_0.23137792370791033","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@dymoo/media-understanding","version":"0.1.3","keywords":["mcp","llm","media","video","audio","transcription","whisper","ffmpeg","opencode","agents"],"author":{"name":"dymoo"},"license":"MIT","_id":"@dymoo/media-understanding@0.1.3","maintainers":[{"name":"dymoo","email":"dyl000@protonmail.com"}],"homepage":"https://github.com/dymoo/media-understanding#readme","bugs":{"url":"https://github.com/dymoo/media-understanding/issues"},"bin":{"media-understanding":"dist/cli.js","media-understanding-mcp":"dist/mcp.js"},"dist":{"shasum":"3094028a07b99ca5a4874a025f0009349492a555","tarball":"https://registry.npmjs.org/@dymoo/media-understanding/-/media-understanding-0.1.3.tgz","fileCount":61,"integrity":"sha512-4NKzuKprm/MOtzWOypVRn7KJR8V4ok/tJW38h0aW6AYxkrinhHPWqdxUa8ixL2pboqoCg+7ruThut4CFMl5edg==","signatures":[{"sig":"MEUCIQD1g/KXCg3wAE1UzsSWbFi5Boh/nes92J5+e9SOdmaGGAIgKEUOyEJv8lqxQsiJABwI/8HOkhDXeK35t8npJPUK2Gg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dymoo%2fmedia-understanding@0.1.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":374733},"type":"module","engines":{"node":">=22"},"exports":{".":"./dist/index.js"},"gitHead":"fa1cd85c2328cfe4eb1673f179e6429f6ff20496","scripts":{"lint":"eslint src","test":"node --test dist/tests/*.test.js","build":"tsc","format":"prettier --write .","pretest":"tsc","check-all":"pnpm run format:check && pnpm run lint && pnpm run typecheck","typecheck":"tsc --noEmit","build:watch":"tsc --watch","postinstall":"node scripts/install-node-av.mjs && node scripts/download-whisper-model.mjs","format:check":"prettier --check .","prepublishOnly":"pnpm run check-all && pnpm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b15edc03-63fc-4fac-9ade-eed16c5d485d"}},"repository":{"url":"git+https://github.com/dymoo/media-understanding.git","type":"git"},"_npmVersion":"11.11.0","description":"MCP server that converts audio/video/image to text + images for LLM consumption","directories":{},"_nodeVersion":"24.14.1","dependencies":{"zod":"^3.24.0","sharp":"^0.34.5","node-av":"^5.2.2","file-type":"^21.3.1","@modelcontextprotocol/sdk":"^1.27.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^10.0.3","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^22.0.0","@types/sharp":"^0.32.0","typescript-eslint":"^8.57.0","eslint-config-prettier":"^10.1.8"},"optionalDependencies":{"@seydx/node-av-linux-x64":"^5.2.2","@seydx/node-av-darwin-x64":"^5.2.2","@seydx/node-av-linux-arm64":"^5.2.2","@seydx/node-av-darwin-arm64":"^5.2.2","@seydx/node-av-win32-x64-msvc":"^5.2.2","@seydx/node-av-win32-x64-mingw":"^5.2.2","@seydx/node-av-win32-arm64-msvc":"^5.2.2","@seydx/node-av-win32-arm64-mingw":"^5.2.2"},"_npmOperationalInternal":{"tmp":"tmp/media-understanding_0.1.3_1775385660103_0.2622533761648038","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"name":"@dymoo/media-understanding","version":"1.0.0","keywords":["mcp","llm","media","video","audio","transcription","whisper","ffmpeg","opencode","agents"],"author":{"name":"dymoo"},"license":"MIT","_id":"@dymoo/media-understanding@1.0.0","maintainers":[{"name":"dymoo","email":"dyl000@protonmail.com"}],"homepage":"https://github.com/dymoo/media-understanding#readme","bugs":{"url":"https://github.com/dymoo/media-understanding/issues"},"bin":{"media-understanding":"dist/cli.js","media-understanding-mcp":"dist/mcp.js"},"dist":{"shasum":"b47f0f9e603e915039e7c54aa4ed3e8654c495cc","tarball":"https://registry.npmjs.org/@dymoo/media-understanding/-/media-understanding-1.0.0.tgz","fileCount":69,"integrity":"sha512-E8FkUIgI+BvrH/9omNPDquuc8K7RIuFG4+DclQvyuUnxz104grFhPUFBR5FK+gcH2cTxfkgK+uJX7Ii9wUcjYQ==","signatures":[{"sig":"MEQCIE5FY1q1C30eXT5g1O49CZ8gnR3LewIiDE1/3MBSQxOZAiBVF8SYx3KsOUrjGAB1MbQDW0arhUJayVjxfbZ5MVQ7Vw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dymoo%2fmedia-understanding@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":446376},"type":"module","engines":{"node":">=22"},"exports":{".":"./dist/index.js"},"gitHead":"9343281c8cbb31f61105fb749ceb80b00e73c97b","scripts":{"lint":"eslint src","test":"node --test dist/tests/*.test.js","build":"tsc","format":"prettier --write .","pretest":"tsc","check-all":"pnpm run format:check && pnpm run lint && pnpm run typecheck","typecheck":"tsc --noEmit","build:watch":"tsc --watch","postinstall":"node scripts/install-node-av.mjs && node scripts/download-whisper-model.mjs","format:check":"prettier --check .","prepublishOnly":"pnpm run check-all && pnpm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b15edc03-63fc-4fac-9ade-eed16c5d485d"}},"repository":{"url":"git+https://github.com/dymoo/media-understanding.git","type":"git"},"_npmVersion":"11.11.0","description":"MCP server that converts audio/video/image to text + images for LLM consumption","directories":{},"_nodeVersion":"24.14.1","dependencies":{"zod":"^3.24.0","sharp":"^0.34.5","node-av":"^5.2.2","file-type":"^21.3.1","@plussub/srt-vtt-parser":"^2.0.5","@modelcontextprotocol/sdk":"^1.27.1"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^10.0.3","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/node":"^22.0.0","@types/sharp":"^0.32.0","typescript-eslint":"^8.57.0","eslint-config-prettier":"^10.1.8"},"optionalDependencies":{"@seydx/node-av-linux-x64":"^5.2.2","@seydx/node-av-darwin-x64":"^5.2.2","@seydx/node-av-linux-arm64":"^5.2.2","@seydx/node-av-darwin-arm64":"^5.2.2","@seydx/node-av-win32-x64-msvc":"^5.2.2","@seydx/node-av-win32-x64-mingw":"^5.2.2","@seydx/node-av-win32-arm64-msvc":"^5.2.2","@seydx/node-av-win32-arm64-mingw":"^5.2.2"},"_npmOperationalInternal":{"tmp":"tmp/media-understanding_1.0.0_1775573726487_0.8631314920184876","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@dymoo/media-understanding","version":"1.1.0","description":"MCP server that converts audio/video/image to text + images for LLM consumption","type":"module","license":"MIT","author":{"name":"dymoo"},"repository":{"type":"git","url":"git+https://github.com/dymoo/media-understanding.git"},"keywords":["mcp","llm","media","video","audio","transcription","whisper","ffmpeg","opencode","agents"],"engines":{"node":">=22"},"exports":{".":"./dist/index.js"},"bin":{"media-understanding":"dist/cli.js","media-understanding-mcp":"dist/mcp.js"},"scripts":{"build":"tsc","build:watch":"tsc --watch","format":"prettier --write .","format:check":"prettier --check .","lint":"eslint src","typecheck":"tsc --noEmit","check-all":"pnpm run format:check && pnpm run lint && pnpm run typecheck","postinstall":"node scripts/install-node-av.mjs && node scripts/download-whisper-model.mjs","test":"node --test dist/tests/*.test.js","pretest":"tsc","prepublishOnly":"pnpm run check-all && pnpm run build"},"dependencies":{"@modelcontextprotocol/sdk":"^1.27.1","@plussub/srt-vtt-parser":"^2.0.5","file-type":"^21.3.1","node-av":"^5.2.2","sharp":"^0.34.5","zod":"^3.24.0"},"optionalDependencies":{"@seydx/node-av-darwin-arm64":"^5.2.2","@seydx/node-av-darwin-x64":"^5.2.2","@seydx/node-av-linux-arm64":"^5.2.2","@seydx/node-av-linux-x64":"^5.2.2","@seydx/node-av-win32-arm64-mingw":"^5.2.2","@seydx/node-av-win32-arm64-msvc":"^5.2.2","@seydx/node-av-win32-x64-mingw":"^5.2.2","@seydx/node-av-win32-x64-msvc":"^5.2.2"},"devDependencies":{"@eslint/js":"^10.0.1","@types/node":"^22.0.0","@types/sharp":"^0.32.0","eslint":"^10.0.3","eslint-config-prettier":"^10.1.8","prettier":"^3.8.1","typescript":"^5.9.3","typescript-eslint":"^8.57.0"},"gitHead":"a37ce994d6feef469bc92495d0562c977e2ee902","_id":"@dymoo/media-understanding@1.1.0","bugs":{"url":"https://github.com/dymoo/media-understanding/issues"},"homepage":"https://github.com/dymoo/media-understanding#readme","_nodeVersion":"24.14.1","_npmVersion":"11.11.0","dist":{"integrity":"sha512-2AZBolXsQ0fPedtWvFHONdMyQu3FXFjFXqmURTRqetXztFrVsvSAwljZD0MZdKC5TeGX69GE7oWgEYWjjwfbUA==","shasum":"b8cb0aab7c8eeb286d402882a3b47bbaae468852","tarball":"https://registry.npmjs.org/@dymoo/media-understanding/-/media-understanding-1.1.0.tgz","fileCount":6,"unpackedSize":31609,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dymoo%2fmedia-understanding@1.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIB77oiq2UKyHnSLEtfmYVvk+rTYhGlbkXgmcgaYK3pC1AiAgbj0WVEcfzneLrLKtRDmGl9TC/I+3Ok5RNJAoaKIB4Q=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b15edc03-63fc-4fac-9ade-eed16c5d485d"}},"directories":{},"maintainers":[{"name":"dymoo","email":"dyl000@protonmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/media-understanding_1.1.0_1775578657707_0.8497644361632679"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-13T13:41:59.394Z","modified":"2026-04-07T16:17:38.146Z","0.1.0":"2026-03-13T13:41:59.743Z","0.1.1":"2026-03-13T13:47:06.634Z","0.1.2":"2026-03-13T15:42:32.336Z","0.1.3":"2026-04-05T10:41:00.247Z","1.0.0":"2026-04-07T14:55:26.727Z","1.1.0":"2026-04-07T16:17:37.865Z"},"bugs":{"url":"https://github.com/dymoo/media-understanding/issues"},"author":{"name":"dymoo"},"license":"MIT","homepage":"https://github.com/dymoo/media-understanding#readme","keywords":["mcp","llm","media","video","audio","transcription","whisper","ffmpeg","opencode","agents"],"repository":{"type":"git","url":"git+https://github.com/dymoo/media-understanding.git"},"description":"MCP server that converts audio/video/image to text + images for LLM consumption","maintainers":[{"name":"dymoo","email":"dyl000@protonmail.com"}],"readme":"# @dymoo/media-understanding\n\nTurn audio, video, and images into the two modalities LLMs actually accept: **text** and **images**.\n\n| Input                       | Output                                             |\n| --------------------------- | -------------------------------------------------- |\n| Audio (mp3, wav, m4a, ...)  | Timestamped transcript text                        |\n| Video (mp4, mkv, mov, ...)  | Transcript text + timestamped keyframe grid images |\n| Image (png, jpg, webp, ...) | Compressed JPEG (base64) + metadata                |\n\n## Agent Setup\n\n<details>\n<summary>OpenClaw</summary>\n\nOpenClaw supports two integration points that work together:\n\n1. **MCP server** — gives the agent 5 media analysis tools in the reply pipeline\n2. **Media model stub** — pre-digests inbound media at conversation start (~50ms probe, no heavy work)\n\n```json5\n{\n  // 1. MCP server — 5 tools for media analysis in the reply pipeline\n  mcpServers: {\n    \"media-understanding\": {\n      command: \"media-understanding-mcp\",\n    },\n  },\n\n  // 2. Media model — pre-digests inbound media before the reply pipeline.\n  //    Runs a quick probe (~50ms) and tells the agent to use MCP tools\n  //    for full analysis. No transcription, no heavy work at pre-digest time.\n  tools: {\n    media: {\n      models: [\n        {\n          type: \"cli\",\n          command: \"media-understanding\",\n          args: [\"{{MediaPath}}\"],\n          capabilities: [\"audio\", \"video\", \"image\"],\n          maxBytes: 10737418240, // 10 GB — let the MCP tools handle size limits\n          timeoutSeconds: 10, // probe is fast\n        },\n      ],\n    },\n  },\n}\n```\n\n**Why this works well:** The media model stub is instant (~50ms header read) and gives the agent a metadata summary plus a nudge to use MCP tools. The agent then has full iterative control — it chooses which tools to call based on the media type and its current task. No API keys needed, works offline, no wasted compute on pre-digestion.\n\n**What the agent sees from the media model stub:**\n\n```\n[Video] 2m15s, 1920x1080, h264/aac, 48.2 MB\n\nThis file has been detected by the media-understanding CLI.\nUse your media-understanding MCP tools for full analysis:\n  - probe_media: batch metadata scanning\n  - understand_media: full analysis (transcript + keyframe grids)\n  - get_transcript: speech content with format options (text/srt/json)\n  - get_video_grids: visual keyframe inspection\n  - get_frames: exact frame extraction at specific timestamps\n```\n\nThe agent then calls MCP tools for the actual work, with full iterative capability.\n\n</details>\n\n<details>\n<summary>OpenCode</summary>\n\nAdd to `~/.config/opencode/opencode.json`:\n\n```json\n{\n  \"mcp\": {\n    \"media-understanding\": {\n      \"type\": \"local\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"-p\", \"@dymoo/media-understanding\", \"media-understanding-mcp\"],\n      \"env\": {\n        \"MEDIA_UNDERSTANDING_MODEL\": \"base.en-q5_1\"\n      }\n    }\n  }\n}\n```\n\n</details>\n\n<details>\n<summary>Claude Desktop</summary>\n\nAdd to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\\Claude\\claude_desktop_config.json` (Windows):\n\n```json\n{\n  \"mcpServers\": {\n    \"media-understanding\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"-p\", \"@dymoo/media-understanding\", \"media-understanding-mcp\"],\n      \"env\": {\n        \"MEDIA_UNDERSTANDING_MODEL\": \"base.en-q5_1\"\n      }\n    }\n  }\n}\n```\n\n</details>\n\n<details>\n<summary>Cursor</summary>\n\nAdd to `.cursor/mcp.json` in your project or `~/.cursor/mcp.json` globally:\n\n```json\n{\n  \"mcpServers\": {\n    \"media-understanding\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"-p\", \"@dymoo/media-understanding\", \"media-understanding-mcp\"],\n      \"env\": {\n        \"MEDIA_UNDERSTANDING_MODEL\": \"base.en-q5_1\"\n      }\n    }\n  }\n}\n```\n\n</details>\n\n<details>\n<summary>Windsurf</summary>\n\nAdd to `~/.codeium/windsurf/mcp_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"media-understanding\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"-p\", \"@dymoo/media-understanding\", \"media-understanding-mcp\"],\n      \"env\": {\n        \"MEDIA_UNDERSTANDING_MODEL\": \"base.en-q5_1\"\n      }\n    }\n  }\n}\n```\n\n</details>\n\n<details>\n<summary>Cline / Roo Code</summary>\n\nAdd a new MCP server:\n\n- command: `npx`\n- args: `-y -p @dymoo/media-understanding media-understanding-mcp`\n- env: `MEDIA_UNDERSTANDING_MODEL=base.en-q5_1`\n\n</details>\n\n## Quick Start\n\n```bash\nnpm install @dymoo/media-understanding\n```\n\nRequirements:\n\n- Node >= 22\n- FFmpeg is handled automatically via `node-av`\n- The default Whisper model (`base.en-q5_1`, ~57 MB) auto-downloads on first transcription; set `SKIP_MODEL_DOWNLOAD=1` to defer\n\n## Docker Quick Start\n\nThe npm package runs the MCP server over **stdio**. The official Docker image wraps that same server with [`supergateway`](https://github.com/supercorp-ai/supergateway) so HTTP-capable MCP clients can connect directly.\n\n```bash\ndocker run --rm -p 8000:8000 ghcr.io/dymoo/media-understanding:1.1.0\n```\n\nEndpoints:\n\n- MCP: `http://localhost:8000/mcp`\n- Health: `http://localhost:8000/healthz`\n\nThe official Docker image includes:\n\n- the built `media-understanding-mcp` server\n- `supergateway@3.4.3` for Streamable HTTP transport\n- the default Whisper model (`base.en-q5_1`)\n- `yt-dlp`, so URL support is enabled out of the box\n\nBind-mount media into the container when you want to analyze local files:\n\n```bash\ndocker run --rm -p 8000:8000 -v \"$PWD/testdata:/media:ro\" ghcr.io/dymoo/media-understanding:1.1.0\n```\n\n### Optional: URL support via yt-dlp\n\nIf [yt-dlp](https://github.com/yt-dlp/yt-dlp) is installed on your system, **all tools** gain the ability to accept URLs in place of file paths. A dedicated `fetch_ytdlp` tool is also registered for fine-grained download control.\n\nyt-dlp is **not bundled by the npm package** — you must install it yourself for local stdio usage. The official Docker image includes yt-dlp so URL support works out of the box.\n\nAs always, URL support does not change your responsibility to comply with local law, content rights, and target-site terms.\n\n```bash\n# macOS\nbrew install yt-dlp\n\n# Linux\npip install yt-dlp     # or: sudo apt install yt-dlp\n\n# Windows\nwinget install yt-dlp   # or: pip install yt-dlp\n```\n\n**Supported platforms** (1800+ total):\nYouTube, **Instagram** (Reels & posts), Vimeo, **Loom**, Twitch, Dailymotion, TikTok, X/Twitter, Facebook, LinkedIn, Reddit, SoundCloud, Dropbox, Google Drive, BBC, CNN, and [many more](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md).\n\nWhen yt-dlp is detected at startup, the server logs `yt-dlp detected — URL support enabled` to stderr. Without it, tools only accept local file paths.\n\n## How It Works\n\nThe server exposes 5 core tools (+ 1 optional) organized around a three-step workflow: **discover, analyze, iterate**. When yt-dlp is installed, all tools also accept URLs.\n\n```\nStep 1: DISCOVER (cheap, batch-safe)\n  probe_media  →  metadata for 1-200 files (~5-50ms each, header reads only)\n\nStep 2: ANALYZE (expensive, one file at a time)\n  understand_media  →  full analysis: metadata + transcript + keyframe grids\n  get_transcript    →  timestamped speech text (text, SRT, or JSON format)\n  get_video_grids   →  visual keyframe contact sheets\n  get_frames        →  exact frames at specific timestamps\n\nStep 3: ITERATE (use output from one tool to target another)\n  transcript timestamps → get_video_grids with start_sec/end_sec\n  grid timestamps → get_frames with exact seconds\n\nOptional (requires yt-dlp):\n  fetch_ytdlp    →  download + cache media from URLs with fine-grained control\n```\n\nThe cost boundary between cheap and expensive operations is encoded in the tool names. `probe_media` is safe to call on dozens of files. The analysis tools process one file per call and enforce payload budgets so an LLM does not accidentally blow up its context.\n\n## MCP Tools\n\n### `probe_media` — discover and triage\n\nScan files for metadata before committing to heavy analysis. Returns type, duration, resolution, codecs, file size. No decoding, no transcription, no images.\n\nAccepts a `paths` parameter: a string or array of strings. Each string can be a literal file path or a glob pattern. Default limit: 50 files (max 200).\n\n```json\n{ \"paths\": \"/path/to/video.mp4\" }\n{ \"paths\": [\"recordings/*.mp4\", \"specific/file.mp3\"] }\n{ \"paths\": \"media/**/*.{mp4,mp3,wav}\", \"max_files\": 100 }\n```\n\n### `understand_media` — full single-file analysis\n\nReturns metadata + transcript + keyframe grids for one file. For video, transcript segments are interleaved with their corresponding grid images — each grid is preceded by the transcript chunk covering that time window, so the LLM sees speech and visuals together in chronological order.\n\nBest for images, short audio, and short-to-medium video. For files over 2 hours, use the focused tools below instead.\n\n```json\n{ \"file_path\": \"/path/to/video.mp4\" }\n{ \"file_path\": \"/path/to/podcast.mp3\", \"model\": \"base.en\" }\n{ \"file_path\": \"/path/to/clip.mp4\", \"start_sec\": 60, \"end_sec\": 120, \"max_grids\": 3 }\n```\n\nKey options: `model`, `max_chars`, `max_total_chars`, `max_grids`, `start_sec`, `end_sec`, `sampling_strategy`, `seconds_per_frame`, `seconds_per_grid`, `cols`, `rows`, `thumb_width`, `aspect_mode`.\n\n### `get_transcript` — speech content with format options\n\nTimestamped transcript with three output formats:\n\n- **`text`** (default) — `[start-end] text` per segment, compact and scannable\n- **`srt`** — standard SRT subtitle format with `HH:MM:SS,mmm` timestamps\n- **`json`** — machine-readable `{ segments: [{ start, end, text }] }` with millisecond precision\n\nOptional time windowing with `start_sec`/`end_sec` filters the output to a specific range (the full file is still transcribed once, then cached).\n\n```json\n{ \"file_path\": \"/path/to/podcast.mp3\" }\n{ \"file_path\": \"/path/to/meeting.mp4\", \"format\": \"srt\" }\n{ \"file_path\": \"/path/to/episode.mp3\", \"format\": \"json\", \"start_sec\": 60, \"end_sec\": 120 }\n```\n\n**Two-step workflow for long media:**\n\n1. **Overview** — `get_transcript(file, { format: \"srt\" })` to scan for topics and transitions\n2. **Detail** — `get_transcript(file, { format: \"json\", start_sec: 120, end_sec: 300 })` for precise segment data in a specific range\n\nThis avoids dumping the entire transcript into context at once.\n\n### `get_video_grids` — visual keyframe sampling\n\nJPEG contact sheets of thumbnails. Every tile has an exact timestamp overlay. Budget-aware: omit `max_grids` and the server auto-fits as many grids as possible within `max_total_chars`.\n\n```json\n{ \"file_path\": \"/path/to/video.mp4\" }\n{ \"file_path\": \"/path/to/movie.mkv\", \"start_sec\": 300, \"end_sec\": 600, \"max_grids\": 2, \"seconds_per_frame\": 8 }\n{ \"file_path\": \"/path/to/lecture.mp4\", \"sampling_strategy\": \"scene\", \"frame_interval\": 150 }\n```\n\n**Example output** — 4x4 grid from a 16:9 landscape video (`thumb_width: 320`):\n\n![Example keyframe grid from get_video_grids — 4x4 contact sheet covering 30-90s with timestamp overlays on each tile](docs/assets/example-grid.jpg)\n\n### `get_frames` — exact moments\n\nOne JPEG per requested timestamp. Each frame includes a timestamp overlay.\n\n```json\n{ \"file_path\": \"/path/to/video.mp4\", \"timestamps\": [0, 30, 60] }\n{ \"file_path\": \"/path/to/clip.mp4\", \"timestamps\": [83.5] }\n```\n\n**Example output** — single frame at t=60s:\n\n![Example frame from get_frames — single frame extracted at 00:01:00.000 with filename and timestamp overlay](docs/assets/example-frame.jpg)\n\n### `fetch_ytdlp` — URL download control (requires yt-dlp)\n\nOnly available when yt-dlp is installed. Fine-grained control over what to download from a URL: subtitles (default, instant), video (for frame analysis), audio (for ASR), thumbnail. All downloads are cached.\n\nFor most workflows, passing a URL directly to `get_transcript` or `understand_media` is simpler — use `fetch_ytdlp` when you need selective downloads.\n\n```json\n{ \"url\": \"https://youtube.com/watch?v=dQw4w9WgXcQ\" }\n{ \"url\": \"https://www.instagram.com/reel/ABC123/\", \"include_audio\": true }\n{ \"url\": \"https://www.loom.com/share/abc123\", \"include_video\": true }\n{ \"url\": \"https://vimeo.com/123456\", \"include_audio\": true }\n```\n\n## Recommended LLM Workflow\n\n### Per-file type guidance\n\n| Media type         | Recommended first tool                                              |\n| ------------------ | ------------------------------------------------------------------- |\n| Image              | `understand_media`                                                  |\n| Short audio (<30m) | `understand_media`                                                  |\n| Long audio         | `get_transcript`                                                    |\n| Short video (<10m) | `understand_media`                                                  |\n| Long video         | `get_transcript` (speech-first) or `get_video_grids` (visual-first) |\n\n### Iteration pattern\n\n1. Start with `understand_media` or `get_transcript` for a first-pass understanding\n2. Use transcript timestamps to target `get_video_grids` on a narrow window\n3. Use grid timestamps to target `get_frames` for exact moments\n\nNarrow the window — don't widen it. Each follow-up call should be more targeted than the last.\n\n### Podcast / long audio workflow\n\nFor analyzing podcasts or long audio files:\n\n1. **Overview pass** — `get_transcript(file, { format: \"srt\" })` to get a timestamped overview of the full episode. Scan the SRT to identify interesting segments, topic transitions, or key moments.\n2. **Detail pass** — `get_transcript(file, { format: \"json\", start_sec: 120, end_sec: 300 })` to get precise segment data for a specific time range. Use for quoting, summarizing, or extracting specific portions.\n\nThe SRT overview is compact and scannable; the JSON detail pass gives exact data for the segments that matter.\n\n## Processing Multiple Files\n\nFor a single file, call `understand_media` directly. For multiple files, follow the three-tier workflow.\n\n### The three-tier approach\n\n```\n1. probe_media({ \"paths\": \"media/**/*.mp4\" })\n   → metadata for all files (cheap, fast)\n\n2. Pick files that matter based on probe results\n\n3. Call analysis tools one file at a time:\n   understand_media({ \"file_path\": \"...\" })\n   get_transcript({ \"file_path\": \"...\" })\n```\n\n### Subagent orchestration (recommended for many files)\n\nWhen processing many files, launch **subagents** — context-isolated workers each with MCP access — instead of accumulating raw media analysis in a single context.\n\n**Why:** Each `understand_media` call returns transcript text and base64 images. Accumulating these for 10+ files floods the context window, causing compression and lost details. Subagents prevent this.\n\n**How:**\n\n1. Probe everything first: `probe_media` with a glob (cheap triage)\n2. Launch one subagent per file (or per small group)\n3. Pass each subagent a clear **intention**: _\"Analyze this podcast episode. Extract the main topics discussed and any action items mentioned.\"_\n4. Each subagent calls `understand_media` / `get_transcript` / etc., then returns a **distilled summary**\n5. The orchestrator synthesizes summaries without ever accumulating raw media context\n\nSubagents are **sacrificial** — their full analysis context is discarded after they return their distilled result. This is the key insight: the orchestrator works with summaries, not raw transcripts and images.\n\n**When NOT to use subagents:**\n\n- Single file — just call `understand_media` directly\n- Quick metadata checks — `probe_media` is sufficient\n- 2-3 small files — serial `understand_media` calls are fine\n\nThis is a **guideline**, not enforcement. The MCP server does not prevent serial `understand_media` calls in one context. But analysis quality degrades as context fills up with raw media data.\n\n## Safety and Budgets\n\nThe server is conservative by default:\n\n- **Payload budgets:** Every response is capped at `max_total_chars` (default 48,000). If a response would exceed the budget, the server returns a natural-language error explaining how to adjust (narrow the window, request fewer images, etc.), including the overage ratio and resolution-aware suggestions for reducing image sizes. Budget guidance is context-aware: grid errors suggest `cols`/`rows`/`thumb_width`, while frame errors suggest fewer timestamps or splitting requests.\n- **Portrait-aware grid defaults:** When grid parameters (`cols`, `rows`, `thumb_width`) are omitted, portrait video (height > width) defaults to `3x3` grids at `120px` thumbnail width instead of the standard `4x4` at `480px`. This prevents portrait video grids from immediately exceeding the default budget. Explicit values always override these defaults.\n- **Preflight checks:** Heavy tools reject obviously problematic requests before doing expensive work:\n  - `understand_media`: files over 2 hours (transcription bottleneck)\n  - `get_transcript`: files over 4 hours\n  - All heavy tools: files over 10 GB\n- **Bounded transcript cache:** In-memory LRU cache (max 32 entries, skips transcripts over 500K chars). Prevents unbounded memory growth.\n- **Concurrency limits:** Heavy operations (transcription + frame extraction) are capped at 2 concurrent in-process jobs to control memory peaks.\n\nWhen the server rejects a request, it explains _why_ and suggests _what to do instead_ — it teaches the calling model to recover.\n\n## Programmatic API\n\n```ts\nimport {\n  extractFrameGridImages,\n  extractFrameImage,\n  probeMedia,\n  transcribeAudio,\n  understandMedia,\n} from \"@dymoo/media-understanding\";\n\nconst info = await probeMedia(\"/path/to/video.mp4\");\nconst segments = await transcribeAudio(\"/path/to/audio.mp3\");\nconst frame = await extractFrameImage(\"/path/to/video.mp4\", 30);\nconst grids = await extractFrameGridImages(\"/path/to/video.mp4\", { maxGrids: 1 });\nconst result = await understandMedia(\"/path/to/video.mp4\");\n```\n\n`understandMedia()` returns `{ info, segments, transcript, grids, gridImages }`.\n\n## Environment Variables\n\n| Variable                         | Default        | Description                                |\n| -------------------------------- | -------------- | ------------------------------------------ |\n| `MEDIA_UNDERSTANDING_MODEL`      | `base.en-q5_1` | Default Whisper model name                 |\n| `MEDIA_UNDERSTANDING_MAX_CHARS`  | `32000`        | Max transcript characters                  |\n| `MEDIA_UNDERSTANDING_MAX_GRIDS`  | `6`            | Max grid images per video call             |\n| `MEDIA_UNDERSTANDING_DISABLE_HW` | (unset)        | Set to `1` to force software decode/encode |\n\n## ASR Model\n\nUses Whisper via `node-av`. Default model: `base.en-q5_1` (English, quantized GGML, ~57 MB). The default model is cached on first transcription call, or prewarmed during Docker image build.\n\nModels are cached at `~/.cache/media-understanding/models/` (or `$XDG_CACHE_HOME/media-understanding/models/`).\n\n## Credits\n\nThanks to [Simon Willison](https://simonwillison.net/) for the inspiration around feeding timestamped video frames to LLMs. This library borrows that spirit — multiple separate images per turn, uniform time-distributed sampling, visible timestamps on every frame — and packages it into a conservative MCP workflow.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}