youtube-knowledge-mcp 2.1.0 → 3.0.0
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/README.md +153 -38
- package/dist/brain-schemas.d.ts +10 -10
- package/dist/brain-schemas.d.ts.map +1 -1
- package/dist/brain-schemas.js +0 -4
- package/dist/brain-schemas.js.map +1 -1
- package/dist/harvest-schemas.d.ts +75 -0
- package/dist/harvest-schemas.d.ts.map +1 -0
- package/dist/harvest-schemas.js +106 -0
- package/dist/harvest-schemas.js.map +1 -0
- package/dist/http/app.d.ts +16 -0
- package/dist/http/app.d.ts.map +1 -0
- package/dist/http/app.js +176 -0
- package/dist/http/app.js.map +1 -0
- package/dist/http/auth.d.ts +38 -0
- package/dist/http/auth.d.ts.map +1 -0
- package/dist/http/auth.js +79 -0
- package/dist/http/auth.js.map +1 -0
- package/dist/http/config.d.ts +41 -0
- package/dist/http/config.d.ts.map +1 -0
- package/dist/http/config.js +65 -0
- package/dist/http/config.js.map +1 -0
- package/dist/http/rate-limiter.d.ts +24 -0
- package/dist/http/rate-limiter.d.ts.map +1 -0
- package/dist/http/rate-limiter.js +55 -0
- package/dist/http/rate-limiter.js.map +1 -0
- package/dist/http/sessions.d.ts +34 -0
- package/dist/http/sessions.d.ts.map +1 -0
- package/dist/http/sessions.js +62 -0
- package/dist/http/sessions.js.map +1 -0
- package/dist/http.d.ts +6 -48
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +35 -252
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -517
- package/dist/index.js.map +1 -1
- package/dist/prompts-brain.d.ts +4 -0
- package/dist/prompts-brain.d.ts.map +1 -0
- package/dist/prompts-brain.js +48 -0
- package/dist/prompts-brain.js.map +1 -0
- package/dist/prompts-research.d.ts +4 -0
- package/dist/prompts-research.d.ts.map +1 -0
- package/dist/prompts-research.js +74 -0
- package/dist/prompts-research.js.map +1 -0
- package/dist/prompts-shared.d.ts +13 -0
- package/dist/prompts-shared.d.ts.map +1 -0
- package/dist/prompts-shared.js +7 -0
- package/dist/prompts-shared.js.map +1 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +5 -100
- package/dist/prompts.js.map +1 -1
- package/dist/registry/brain-tools.d.ts +4 -0
- package/dist/registry/brain-tools.d.ts.map +1 -0
- package/dist/registry/brain-tools.js +104 -0
- package/dist/registry/brain-tools.js.map +1 -0
- package/dist/registry/channel-tools.d.ts +4 -0
- package/dist/registry/channel-tools.d.ts.map +1 -0
- package/dist/registry/channel-tools.js +53 -0
- package/dist/registry/channel-tools.js.map +1 -0
- package/dist/registry/harvest-tools.d.ts +10 -0
- package/dist/registry/harvest-tools.d.ts.map +1 -0
- package/dist/registry/harvest-tools.js +109 -0
- package/dist/registry/harvest-tools.js.map +1 -0
- package/dist/registry/health-tools.d.ts +4 -0
- package/dist/registry/health-tools.d.ts.map +1 -0
- package/dist/registry/health-tools.js +39 -0
- package/dist/registry/health-tools.js.map +1 -0
- package/dist/registry/index.d.ts +8 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +29 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/library-tools.d.ts +4 -0
- package/dist/registry/library-tools.d.ts.map +1 -0
- package/dist/registry/library-tools.js +116 -0
- package/dist/registry/library-tools.js.map +1 -0
- package/dist/registry/media-tools.d.ts +4 -0
- package/dist/registry/media-tools.d.ts.map +1 -0
- package/dist/registry/media-tools.js +100 -0
- package/dist/registry/media-tools.js.map +1 -0
- package/dist/registry/thumbnail-tools.d.ts +7 -0
- package/dist/registry/thumbnail-tools.d.ts.map +1 -0
- package/dist/registry/thumbnail-tools.js +72 -0
- package/dist/registry/thumbnail-tools.js.map +1 -0
- package/dist/registry/transcript-tools.d.ts +4 -0
- package/dist/registry/transcript-tools.d.ts.map +1 -0
- package/dist/registry/transcript-tools.js +68 -0
- package/dist/registry/transcript-tools.js.map +1 -0
- package/dist/registry/types.d.ts +41 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +17 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/registry/video-tools.d.ts +4 -0
- package/dist/registry/video-tools.d.ts.map +1 -0
- package/dist/registry/video-tools.js +101 -0
- package/dist/registry/video-tools.js.map +1 -0
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +13 -10
- package/dist/resources.js.map +1 -1
- package/dist/schemas.d.ts +250 -2
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +61 -0
- package/dist/schemas.js.map +1 -1
- package/dist/thumbnail-resource.d.ts +8 -0
- package/dist/thumbnail-resource.d.ts.map +1 -0
- package/dist/thumbnail-resource.js +38 -0
- package/dist/thumbnail-resource.js.map +1 -0
- package/dist/thumbnail-schemas.d.ts +512 -0
- package/dist/thumbnail-schemas.d.ts.map +1 -0
- package/dist/thumbnail-schemas.js +95 -0
- package/dist/thumbnail-schemas.js.map +1 -0
- package/dist/tools/ask-brain.js +1 -1
- package/dist/tools/ask-brain.js.map +1 -1
- package/dist/tools/batch.js +1 -1
- package/dist/tools/batch.js.map +1 -1
- package/dist/tools/brain-info.js +1 -1
- package/dist/tools/brain-info.js.map +1 -1
- package/dist/tools/build-brain.js +1 -1
- package/dist/tools/build-brain.js.map +1 -1
- package/dist/tools/check-health.d.ts +61 -0
- package/dist/tools/check-health.d.ts.map +1 -1
- package/dist/tools/check-health.js +94 -6
- package/dist/tools/check-health.js.map +1 -1
- package/dist/tools/clips.d.ts +9 -41
- package/dist/tools/clips.d.ts.map +1 -1
- package/dist/tools/clips.js +52 -128
- package/dist/tools/clips.js.map +1 -1
- package/dist/tools/fetch-channel-thumbnails.d.ts +179 -0
- package/dist/tools/fetch-channel-thumbnails.d.ts.map +1 -0
- package/dist/tools/fetch-channel-thumbnails.js +123 -0
- package/dist/tools/fetch-channel-thumbnails.js.map +1 -0
- package/dist/tools/fetch-videos.d.ts +18 -4
- package/dist/tools/fetch-videos.d.ts.map +1 -1
- package/dist/tools/fetch-videos.js +64 -20
- package/dist/tools/fetch-videos.js.map +1 -1
- package/dist/tools/frames-subtitles.d.ts +44 -0
- package/dist/tools/frames-subtitles.d.ts.map +1 -0
- package/dist/tools/frames-subtitles.js +104 -0
- package/dist/tools/frames-subtitles.js.map +1 -0
- package/dist/tools/get-channel-info.d.ts +2 -0
- package/dist/tools/get-channel-info.d.ts.map +1 -1
- package/dist/tools/get-channel-info.js +8 -1
- package/dist/tools/get-channel-info.js.map +1 -1
- package/dist/tools/get-comments.d.ts +238 -6
- package/dist/tools/get-comments.d.ts.map +1 -1
- package/dist/tools/get-comments.js +85 -21
- package/dist/tools/get-comments.js.map +1 -1
- package/dist/tools/get-coverage.d.ts +122 -0
- package/dist/tools/get-coverage.d.ts.map +1 -0
- package/dist/tools/get-coverage.js +105 -0
- package/dist/tools/get-coverage.js.map +1 -0
- package/dist/tools/get-playlist-info.d.ts.map +1 -1
- package/dist/tools/get-playlist-info.js +1 -0
- package/dist/tools/get-playlist-info.js.map +1 -1
- package/dist/tools/get-thumbnail.d.ts +38 -0
- package/dist/tools/get-thumbnail.d.ts.map +1 -0
- package/dist/tools/get-thumbnail.js +152 -0
- package/dist/tools/get-thumbnail.js.map +1 -0
- package/dist/tools/get-transcript.d.ts.map +1 -1
- package/dist/tools/get-transcript.js +2 -14
- package/dist/tools/get-transcript.js.map +1 -1
- package/dist/tools/harvest-channel.d.ts +92 -0
- package/dist/tools/harvest-channel.d.ts.map +1 -0
- package/dist/tools/harvest-channel.js +62 -0
- package/dist/tools/harvest-channel.js.map +1 -0
- package/dist/tools/harvest-comments.d.ts +81 -0
- package/dist/tools/harvest-comments.d.ts.map +1 -0
- package/dist/tools/harvest-comments.js +115 -0
- package/dist/tools/harvest-comments.js.map +1 -0
- package/dist/tools/library.d.ts.map +1 -1
- package/dist/tools/library.js +14 -4
- package/dist/tools/library.js.map +1 -1
- package/dist/tools/list-channel-thumbnails.d.ts +235 -0
- package/dist/tools/list-channel-thumbnails.d.ts.map +1 -0
- package/dist/tools/list-channel-thumbnails.js +79 -0
- package/dist/tools/list-channel-thumbnails.js.map +1 -0
- package/dist/tools/list-library.js +1 -1
- package/dist/tools/list-library.js.map +1 -1
- package/dist/tools/prune-harvest.d.ts +59 -0
- package/dist/tools/prune-harvest.d.ts.map +1 -0
- package/dist/tools/prune-harvest.js +108 -0
- package/dist/tools/prune-harvest.js.map +1 -0
- package/dist/tools/query-comments.d.ts +125 -0
- package/dist/tools/query-comments.d.ts.map +1 -0
- package/dist/tools/query-comments.js +106 -0
- package/dist/tools/query-comments.js.map +1 -0
- package/dist/tools/query-videos.d.ts +118 -0
- package/dist/tools/query-videos.d.ts.map +1 -0
- package/dist/tools/query-videos.js +97 -0
- package/dist/tools/query-videos.js.map +1 -0
- package/dist/tools/repair-store.d.ts +34 -0
- package/dist/tools/repair-store.d.ts.map +1 -0
- package/dist/tools/repair-store.js +58 -0
- package/dist/tools/repair-store.js.map +1 -0
- package/dist/tools/save-to-library.d.ts.map +1 -1
- package/dist/tools/save-to-library.js +5 -1
- package/dist/tools/save-to-library.js.map +1 -1
- package/dist/tools/search-channels.d.ts +6 -0
- package/dist/tools/search-channels.d.ts.map +1 -1
- package/dist/tools/search-channels.js +5 -1
- package/dist/tools/search-channels.js.map +1 -1
- package/dist/tools/search-transcript.d.ts.map +1 -1
- package/dist/tools/search-transcript.js +6 -13
- package/dist/tools/search-transcript.js.map +1 -1
- package/dist/tools/search-videos.d.ts +3 -0
- package/dist/tools/search-videos.d.ts.map +1 -1
- package/dist/tools/search-videos.js +1 -1
- package/dist/tools/search-videos.js.map +1 -1
- package/dist/utils/batches.d.ts +15 -0
- package/dist/utils/batches.d.ts.map +1 -0
- package/dist/utils/batches.js +31 -0
- package/dist/utils/batches.js.map +1 -0
- package/dist/utils/brain-build.d.ts.map +1 -1
- package/dist/utils/brain-build.js +1 -12
- package/dist/utils/brain-build.js.map +1 -1
- package/dist/utils/brain-index.d.ts.map +1 -1
- package/dist/utils/brain-index.js +3 -2
- package/dist/utils/brain-index.js.map +1 -1
- package/dist/utils/brain-lock.d.ts +2 -18
- package/dist/utils/brain-lock.d.ts.map +1 -1
- package/dist/utils/brain-lock.js +4 -80
- package/dist/utils/brain-lock.js.map +1 -1
- package/dist/utils/brain-lookup.d.ts +8 -0
- package/dist/utils/brain-lookup.d.ts.map +1 -1
- package/dist/utils/brain-lookup.js +5 -31
- package/dist/utils/brain-lookup.js.map +1 -1
- package/dist/utils/brain-storage.d.ts.map +1 -1
- package/dist/utils/brain-storage.js +5 -5
- package/dist/utils/brain-storage.js.map +1 -1
- package/dist/utils/caption-probe.d.ts +7 -0
- package/dist/utils/caption-probe.d.ts.map +1 -0
- package/dist/utils/caption-probe.js +36 -0
- package/dist/utils/caption-probe.js.map +1 -0
- package/dist/utils/channel-images.d.ts +16 -0
- package/dist/utils/channel-images.d.ts.map +1 -0
- package/dist/utils/channel-images.js +35 -0
- package/dist/utils/channel-images.js.map +1 -0
- package/dist/utils/channel-lookup.d.ts +5 -0
- package/dist/utils/channel-lookup.d.ts.map +1 -0
- package/dist/utils/channel-lookup.js +37 -0
- package/dist/utils/channel-lookup.js.map +1 -0
- package/dist/utils/chapters.d.ts +11 -0
- package/dist/utils/chapters.d.ts.map +1 -0
- package/dist/utils/chapters.js +24 -0
- package/dist/utils/chapters.js.map +1 -0
- package/dist/utils/clips.d.ts.map +1 -1
- package/dist/utils/clips.js +18 -26
- package/dist/utils/clips.js.map +1 -1
- package/dist/utils/comment-store.d.ts +71 -0
- package/dist/utils/comment-store.d.ts.map +1 -0
- package/dist/utils/comment-store.js +129 -0
- package/dist/utils/comment-store.js.map +1 -0
- package/dist/utils/comment-threads.d.ts +51 -0
- package/dist/utils/comment-threads.d.ts.map +1 -0
- package/dist/utils/comment-threads.js +76 -0
- package/dist/utils/comment-threads.js.map +1 -0
- package/dist/utils/coverage-text.d.ts +3 -0
- package/dist/utils/coverage-text.d.ts.map +1 -0
- package/dist/utils/coverage-text.js +62 -0
- package/dist/utils/coverage-text.js.map +1 -0
- package/dist/utils/coverage.d.ts +57 -0
- package/dist/utils/coverage.d.ts.map +1 -0
- package/dist/utils/coverage.js +142 -0
- package/dist/utils/coverage.js.map +1 -0
- package/dist/utils/env.d.ts +26 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +72 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/errors.d.ts +1 -1
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +28 -2
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/file-lock.d.ts +34 -0
- package/dist/utils/file-lock.d.ts.map +1 -0
- package/dist/utils/file-lock.js +91 -0
- package/dist/utils/file-lock.js.map +1 -0
- package/dist/utils/flat-listing.d.ts +89 -0
- package/dist/utils/flat-listing.d.ts.map +1 -0
- package/dist/utils/flat-listing.js +68 -0
- package/dist/utils/flat-listing.js.map +1 -0
- package/dist/utils/format.d.ts +18 -4
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +2 -6
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/guard.d.ts +21 -0
- package/dist/utils/guard.d.ts.map +1 -0
- package/dist/utils/guard.js +105 -0
- package/dist/utils/guard.js.map +1 -0
- package/dist/utils/harvest-catalog.d.ts +44 -0
- package/dist/utils/harvest-catalog.d.ts.map +1 -0
- package/dist/utils/harvest-catalog.js +164 -0
- package/dist/utils/harvest-catalog.js.map +1 -0
- package/dist/utils/harvest-lock.d.ts +3 -0
- package/dist/utils/harvest-lock.d.ts.map +1 -0
- package/dist/utils/harvest-lock.js +26 -0
- package/dist/utils/harvest-lock.js.map +1 -0
- package/dist/utils/harvest-receipts.d.ts +14 -0
- package/dist/utils/harvest-receipts.d.ts.map +1 -0
- package/dist/utils/harvest-receipts.js +110 -0
- package/dist/utils/harvest-receipts.js.map +1 -0
- package/dist/utils/image-dimensions.d.ts +19 -0
- package/dist/utils/image-dimensions.d.ts.map +1 -0
- package/dist/utils/image-dimensions.js +93 -0
- package/dist/utils/image-dimensions.js.map +1 -0
- package/dist/utils/image-fetch.d.ts +31 -0
- package/dist/utils/image-fetch.d.ts.map +1 -0
- package/dist/utils/image-fetch.js +141 -0
- package/dist/utils/image-fetch.js.map +1 -0
- package/dist/utils/json-file.d.ts +14 -5
- package/dist/utils/json-file.d.ts.map +1 -1
- package/dist/utils/json-file.js +24 -9
- package/dist/utils/json-file.js.map +1 -1
- package/dist/utils/listing-cursor.d.ts +31 -0
- package/dist/utils/listing-cursor.d.ts.map +1 -0
- package/dist/utils/listing-cursor.js +39 -0
- package/dist/utils/listing-cursor.js.map +1 -0
- package/dist/utils/paths.d.ts +10 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +14 -0
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/pattern.d.ts +30 -0
- package/dist/utils/pattern.d.ts.map +1 -0
- package/dist/utils/pattern.js +130 -0
- package/dist/utils/pattern.js.map +1 -0
- package/dist/utils/pot-preflight.d.ts +53 -0
- package/dist/utils/pot-preflight.d.ts.map +1 -0
- package/dist/utils/pot-preflight.js +146 -0
- package/dist/utils/pot-preflight.js.map +1 -0
- package/dist/utils/search-index.d.ts.map +1 -1
- package/dist/utils/search-index.js +9 -4
- package/dist/utils/search-index.js.map +1 -1
- package/dist/utils/sleep.d.ts +9 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/utils/sleep.js +25 -0
- package/dist/utils/sleep.js.map +1 -0
- package/dist/utils/storage.d.ts.map +1 -1
- package/dist/utils/storage.js +14 -8
- package/dist/utils/storage.js.map +1 -1
- package/dist/utils/store-health.d.ts +37 -0
- package/dist/utils/store-health.d.ts.map +1 -0
- package/dist/utils/store-health.js +92 -0
- package/dist/utils/store-health.js.map +1 -0
- package/dist/utils/store-migrations.d.ts +33 -0
- package/dist/utils/store-migrations.d.ts.map +1 -0
- package/dist/utils/store-migrations.js +41 -0
- package/dist/utils/store-migrations.js.map +1 -0
- package/dist/utils/store-paths.d.ts +33 -0
- package/dist/utils/store-paths.d.ts.map +1 -0
- package/dist/utils/store-paths.js +48 -0
- package/dist/utils/store-paths.js.map +1 -0
- package/dist/utils/store-rows.d.ts +22 -0
- package/dist/utils/store-rows.d.ts.map +1 -0
- package/dist/utils/store-rows.js +62 -0
- package/dist/utils/store-rows.js.map +1 -0
- package/dist/utils/store-schema.d.ts +16 -0
- package/dist/utils/store-schema.d.ts.map +1 -0
- package/dist/utils/store-schema.js +168 -0
- package/dist/utils/store-schema.js.map +1 -0
- package/dist/utils/store.d.ts +34 -0
- package/dist/utils/store.d.ts.map +1 -0
- package/dist/utils/store.js +157 -0
- package/dist/utils/store.js.map +1 -0
- package/dist/utils/subtitles.d.ts +7 -0
- package/dist/utils/subtitles.d.ts.map +1 -0
- package/dist/utils/subtitles.js +30 -0
- package/dist/utils/subtitles.js.map +1 -0
- package/dist/utils/thumbnail-entry.d.ts +19 -0
- package/dist/utils/thumbnail-entry.d.ts.map +1 -0
- package/dist/utils/thumbnail-entry.js +119 -0
- package/dist/utils/thumbnail-entry.js.map +1 -0
- package/dist/utils/thumbnail-fetch.d.ts +37 -0
- package/dist/utils/thumbnail-fetch.d.ts.map +1 -0
- package/dist/utils/thumbnail-fetch.js +89 -0
- package/dist/utils/thumbnail-fetch.js.map +1 -0
- package/dist/utils/thumbnail-ladder.d.ts +44 -0
- package/dist/utils/thumbnail-ladder.d.ts.map +1 -0
- package/dist/utils/thumbnail-ladder.js +68 -0
- package/dist/utils/thumbnail-ladder.js.map +1 -0
- package/dist/utils/thumbnail-listing.d.ts +16 -0
- package/dist/utils/thumbnail-listing.d.ts.map +1 -0
- package/dist/utils/thumbnail-listing.js +26 -0
- package/dist/utils/thumbnail-listing.js.map +1 -0
- package/dist/utils/thumbnail-paths.d.ts +20 -0
- package/dist/utils/thumbnail-paths.d.ts.map +1 -0
- package/dist/utils/thumbnail-paths.js +35 -0
- package/dist/utils/thumbnail-paths.js.map +1 -0
- package/dist/utils/thumbnail-store.d.ts +19 -0
- package/dist/utils/thumbnail-store.d.ts.map +1 -0
- package/dist/utils/thumbnail-store.js +125 -0
- package/dist/utils/thumbnail-store.js.map +1 -0
- package/dist/utils/transcript-cache.d.ts +20 -0
- package/dist/utils/transcript-cache.d.ts.map +1 -0
- package/dist/utils/transcript-cache.js +127 -0
- package/dist/utils/transcript-cache.js.map +1 -0
- package/dist/utils/transcript-search.d.ts +20 -0
- package/dist/utils/transcript-search.d.ts.map +1 -0
- package/dist/utils/transcript-search.js +35 -0
- package/dist/utils/transcript-search.js.map +1 -0
- package/dist/utils/transcript.d.ts +10 -31
- package/dist/utils/transcript.d.ts.map +1 -1
- package/dist/utils/transcript.js +2 -64
- package/dist/utils/transcript.js.map +1 -1
- package/dist/utils/validate-youtube.d.ts +35 -0
- package/dist/utils/validate-youtube.d.ts.map +1 -0
- package/dist/utils/validate-youtube.js +87 -0
- package/dist/utils/validate-youtube.js.map +1 -0
- package/dist/utils/validate.d.ts +11 -4
- package/dist/utils/validate.d.ts.map +1 -1
- package/dist/utils/validate.js +43 -36
- package/dist/utils/validate.js.map +1 -1
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +19 -0
- package/dist/utils/version.js.map +1 -0
- package/dist/utils/video-stats-cache.d.ts +16 -0
- package/dist/utils/video-stats-cache.d.ts.map +1 -0
- package/dist/utils/video-stats-cache.js +81 -0
- package/dist/utils/video-stats-cache.js.map +1 -0
- package/dist/utils/video-stats.d.ts +186 -0
- package/dist/utils/video-stats.d.ts.map +1 -0
- package/dist/utils/video-stats.js +168 -0
- package/dist/utils/video-stats.js.map +1 -0
- package/dist/utils/video-store.d.ts +69 -0
- package/dist/utils/video-store.d.ts.map +1 -0
- package/dist/utils/video-store.js +77 -0
- package/dist/utils/video-store.js.map +1 -0
- package/dist/utils/youtube-channel.d.ts +51 -0
- package/dist/utils/youtube-channel.d.ts.map +1 -0
- package/dist/utils/youtube-channel.js +91 -0
- package/dist/utils/youtube-channel.js.map +1 -0
- package/dist/utils/youtube-download.d.ts +27 -0
- package/dist/utils/youtube-download.d.ts.map +1 -0
- package/dist/utils/youtube-download.js +118 -0
- package/dist/utils/youtube-download.js.map +1 -0
- package/dist/utils/youtube-schemas.d.ts +492 -0
- package/dist/utils/youtube-schemas.d.ts.map +1 -0
- package/dist/utils/youtube-schemas.js +148 -0
- package/dist/utils/youtube-schemas.js.map +1 -0
- package/dist/utils/youtube-search.d.ts +14 -0
- package/dist/utils/youtube-search.d.ts.map +1 -0
- package/dist/utils/youtube-search.js +39 -0
- package/dist/utils/youtube-search.js.map +1 -0
- package/dist/utils/youtube-url.d.ts +25 -0
- package/dist/utils/youtube-url.d.ts.map +1 -0
- package/dist/utils/youtube-url.js +75 -0
- package/dist/utils/youtube-url.js.map +1 -0
- package/dist/utils/youtube-video.d.ts +68 -0
- package/dist/utils/youtube-video.d.ts.map +1 -0
- package/dist/utils/youtube-video.js +110 -0
- package/dist/utils/youtube-video.js.map +1 -0
- package/dist/utils/youtube.d.ts +10 -122
- package/dist/utils/youtube.d.ts.map +1 -1
- package/dist/utils/youtube.js +11 -486
- package/dist/utils/youtube.js.map +1 -1
- package/dist/utils/ytdlp-env.d.ts +33 -0
- package/dist/utils/ytdlp-env.d.ts.map +1 -0
- package/dist/utils/ytdlp-env.js +121 -0
- package/dist/utils/ytdlp-env.js.map +1 -0
- package/dist/utils/ytdlp-limiter.d.ts +27 -0
- package/dist/utils/ytdlp-limiter.d.ts.map +1 -0
- package/dist/utils/ytdlp-limiter.js +85 -0
- package/dist/utils/ytdlp-limiter.js.map +1 -0
- package/dist/utils/ytdlp-pacer.d.ts +64 -0
- package/dist/utils/ytdlp-pacer.d.ts.map +1 -0
- package/dist/utils/ytdlp-pacer.js +165 -0
- package/dist/utils/ytdlp-pacer.js.map +1 -0
- package/dist/utils/ytdlp-parse.d.ts +13 -0
- package/dist/utils/ytdlp-parse.d.ts.map +1 -0
- package/dist/utils/ytdlp-parse.js +48 -0
- package/dist/utils/ytdlp-parse.js.map +1 -0
- package/dist/utils/ytdlp-stream.d.ts +32 -0
- package/dist/utils/ytdlp-stream.d.ts.map +1 -0
- package/dist/utils/ytdlp-stream.js +61 -0
- package/dist/utils/ytdlp-stream.js.map +1 -0
- package/dist/utils/ytdlp.d.ts +23 -18
- package/dist/utils/ytdlp.d.ts.map +1 -1
- package/dist/utils/ytdlp.js +56 -134
- package/dist/utils/ytdlp.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { join } from 'path';
|
|
2
|
+
import { readFile, unlink } from 'fs/promises';
|
|
3
|
+
import { existsSync, readdirSync } from 'fs';
|
|
4
|
+
import { YouTubeError } from './errors.js';
|
|
5
|
+
import { TIMEOUTS, runYtDlp } from './ytdlp.js';
|
|
6
|
+
import { noCaptionsError } from './caption-probe.js';
|
|
7
|
+
import { assertLanguageTag } from './validate.js';
|
|
8
|
+
import { envInt } from './env.js';
|
|
9
|
+
import { dataDir, ensurePrivateDir } from './paths.js';
|
|
10
|
+
import { writeJsonAtomic } from './json-file.js';
|
|
11
|
+
import { TRANSCRIPT_CACHE_VERSION, cachedTranscriptSchema, parseVtt, segmentsToText, } from './transcript.js';
|
|
12
|
+
import { extractVideoId, watchUrl } from './youtube-url.js';
|
|
13
|
+
/** Fetching a transcript from YouTube and keeping it on disk. */
|
|
14
|
+
const CACHE_DIR = dataDir('transcripts');
|
|
15
|
+
/** Captions rarely change, but a cache with no expiry is a cache that goes wrong. */
|
|
16
|
+
const TRANSCRIPT_TTL_MS = envInt('YOUTUBE_MCP_TRANSCRIPT_TTL_MS', 30 * 86_400_000, { min: 0 });
|
|
17
|
+
function cachePath(videoId, language) {
|
|
18
|
+
return join(CACHE_DIR, `${videoId}.${language}.json`);
|
|
19
|
+
}
|
|
20
|
+
async function readCachedTranscript(videoId, language) {
|
|
21
|
+
const path = cachePath(videoId, language);
|
|
22
|
+
if (!existsSync(path))
|
|
23
|
+
return undefined;
|
|
24
|
+
try {
|
|
25
|
+
const parsed = cachedTranscriptSchema.safeParse(JSON.parse(await readFile(path, 'utf-8')));
|
|
26
|
+
// Anything unreadable — an older layout, a truncated write, a hand-edit —
|
|
27
|
+
// is treated as absent and refetched, rather than half-trusted.
|
|
28
|
+
if (!parsed.success)
|
|
29
|
+
return undefined;
|
|
30
|
+
const cached = parsed.data;
|
|
31
|
+
// A cache written by an older version holds untimed text; regenerate it
|
|
32
|
+
// rather than reading it back as if it had timings.
|
|
33
|
+
if (cached.version !== TRANSCRIPT_CACHE_VERSION)
|
|
34
|
+
return undefined;
|
|
35
|
+
const fetchedAt = Date.parse(cached.fetchedAt);
|
|
36
|
+
if (Number.isFinite(fetchedAt) && Date.now() - fetchedAt > TRANSCRIPT_TTL_MS)
|
|
37
|
+
return undefined;
|
|
38
|
+
return cached;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** True when a usable transcript is already on disk, in any language. */
|
|
45
|
+
export function hasCachedTranscript(videoId) {
|
|
46
|
+
if (!existsSync(CACHE_DIR))
|
|
47
|
+
return false;
|
|
48
|
+
return readdirSync(CACHE_DIR).some((name) => name.startsWith(`${videoId}.`) && name.endsWith('.json'));
|
|
49
|
+
}
|
|
50
|
+
export async function getTranscript(urlOrId, options = {}) {
|
|
51
|
+
const preferredLang = options.language ?? 'en';
|
|
52
|
+
const videoId = extractVideoId(urlOrId);
|
|
53
|
+
assertLanguageTag(preferredLang);
|
|
54
|
+
const url = watchUrl(videoId);
|
|
55
|
+
await ensurePrivateDir(CACHE_DIR);
|
|
56
|
+
if (!options.refresh) {
|
|
57
|
+
const cached = await readCachedTranscript(videoId, preferredLang);
|
|
58
|
+
if (cached) {
|
|
59
|
+
return {
|
|
60
|
+
transcript: segmentsToText(cached.segments),
|
|
61
|
+
segments: cached.segments,
|
|
62
|
+
language: cached.language,
|
|
63
|
+
videoId,
|
|
64
|
+
cached: true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const tempDir = join(CACHE_DIR, 'temp');
|
|
69
|
+
await ensurePrivateDir(tempDir);
|
|
70
|
+
const outputTemplate = join(tempDir, videoId);
|
|
71
|
+
try {
|
|
72
|
+
await runYtDlp([
|
|
73
|
+
'--skip-download',
|
|
74
|
+
'--write-auto-sub',
|
|
75
|
+
'--write-sub',
|
|
76
|
+
'--sub-lang',
|
|
77
|
+
`${preferredLang},${preferredLang}-orig`,
|
|
78
|
+
'--sub-format',
|
|
79
|
+
'vtt',
|
|
80
|
+
'--convert-subs',
|
|
81
|
+
'vtt',
|
|
82
|
+
'-o',
|
|
83
|
+
outputTemplate,
|
|
84
|
+
], { label: 'get_transcript', timeoutMs: TIMEOUTS.transcript, target: url });
|
|
85
|
+
const candidates = [
|
|
86
|
+
{ file: `${outputTemplate}.${preferredLang}.vtt`, language: preferredLang },
|
|
87
|
+
{
|
|
88
|
+
file: `${outputTemplate}.${preferredLang}-orig.vtt`,
|
|
89
|
+
language: `${preferredLang} (auto-generated)`,
|
|
90
|
+
},
|
|
91
|
+
{ file: `${outputTemplate}.en.vtt`, language: 'en' },
|
|
92
|
+
];
|
|
93
|
+
const found = candidates.find((candidate) => existsSync(candidate.file));
|
|
94
|
+
if (!found)
|
|
95
|
+
throw await noCaptionsError(url, preferredLang);
|
|
96
|
+
const segments = parseVtt(await readFile(found.file, 'utf-8'));
|
|
97
|
+
if (segments.length === 0)
|
|
98
|
+
throw await noCaptionsError(url, preferredLang);
|
|
99
|
+
const entry = {
|
|
100
|
+
version: TRANSCRIPT_CACHE_VERSION,
|
|
101
|
+
videoId,
|
|
102
|
+
language: found.language,
|
|
103
|
+
fetchedAt: new Date().toISOString(),
|
|
104
|
+
segments,
|
|
105
|
+
};
|
|
106
|
+
await writeJsonAtomic(cachePath(videoId, preferredLang), entry, { pretty: false });
|
|
107
|
+
await unlink(found.file).catch(() => undefined);
|
|
108
|
+
return {
|
|
109
|
+
transcript: segmentsToText(segments),
|
|
110
|
+
segments,
|
|
111
|
+
language: found.language,
|
|
112
|
+
videoId,
|
|
113
|
+
cached: false,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
// Already typed and actionable — pass it through rather than flattening it
|
|
118
|
+
// into a generic string, which is what the old wrapper did to every failure.
|
|
119
|
+
if (error instanceof YouTubeError)
|
|
120
|
+
throw error;
|
|
121
|
+
throw new YouTubeError('YTDLP_FAILED', `Could not read the transcript for ${videoId}.`, {
|
|
122
|
+
nextStep: 'Verify the video exists and has captions, or call check_health.',
|
|
123
|
+
cause: error,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=transcript-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-cache.js","sourceRoot":"","sources":["../../src/utils/transcript-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TranscriptSegment } from './transcript.js';
|
|
2
|
+
/** Finding a phrase in a transcript. */
|
|
3
|
+
export interface TranscriptMatch {
|
|
4
|
+
segment: TranscriptSegment;
|
|
5
|
+
/** The matched text, for highlighting. */
|
|
6
|
+
match: string;
|
|
7
|
+
url: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Find a compiled pattern in a transcript.
|
|
11
|
+
*
|
|
12
|
+
* Matching runs over the joined text of a small window of segments, not each
|
|
13
|
+
* segment alone, so a phrase split across a caption boundary is still found —
|
|
14
|
+
* which is most phrases, since caption breaks fall mid-sentence.
|
|
15
|
+
*
|
|
16
|
+
* The pattern arrives compiled (see `compilePattern`), so it was validated
|
|
17
|
+
* exactly once, with the flags in force here.
|
|
18
|
+
*/
|
|
19
|
+
export declare function searchSegments(segments: TranscriptSegment[], pattern: RegExp, limit?: number): TranscriptMatch[];
|
|
20
|
+
//# sourceMappingURL=transcript-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-search.d.ts","sourceRoot":"","sources":["../../src/utils/transcript-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,wCAAwC;AAExC,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,OAAO,EAAE,MAAM,EACf,KAAK,SAAK,GACT,eAAe,EAAE,CA0BnB"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find a compiled pattern in a transcript.
|
|
3
|
+
*
|
|
4
|
+
* Matching runs over the joined text of a small window of segments, not each
|
|
5
|
+
* segment alone, so a phrase split across a caption boundary is still found —
|
|
6
|
+
* which is most phrases, since caption breaks fall mid-sentence.
|
|
7
|
+
*
|
|
8
|
+
* The pattern arrives compiled (see `compilePattern`), so it was validated
|
|
9
|
+
* exactly once, with the flags in force here.
|
|
10
|
+
*/
|
|
11
|
+
export function searchSegments(segments, pattern, limit = 20) {
|
|
12
|
+
const matches = [];
|
|
13
|
+
for (let index = 0; index < segments.length && matches.length < limit; index++) {
|
|
14
|
+
const current = segments[index];
|
|
15
|
+
if (!current)
|
|
16
|
+
continue;
|
|
17
|
+
// Two segments of lookahead covers phrases spanning a caption break.
|
|
18
|
+
const haystack = segments
|
|
19
|
+
.slice(index, index + 3)
|
|
20
|
+
.map((segment) => segment.text)
|
|
21
|
+
.join(' ');
|
|
22
|
+
pattern.lastIndex = 0;
|
|
23
|
+
const found = pattern.exec(haystack);
|
|
24
|
+
if (!found)
|
|
25
|
+
continue;
|
|
26
|
+
// The lookahead window means a hit can start in a *later* segment. Only
|
|
27
|
+
// accept it here if it begins inside this one; otherwise the same hit is
|
|
28
|
+
// reported once per preceding segment, and attributed to the wrong time.
|
|
29
|
+
if (found.index > current.text.length)
|
|
30
|
+
continue;
|
|
31
|
+
matches.push({ segment: current, match: found[0], url: '' });
|
|
32
|
+
}
|
|
33
|
+
return matches;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=transcript-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-search.js","sourceRoot":"","sources":["../../src/utils/transcript-search.ts"],"names":[],"mappings":"AAWA;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC5B,QAA6B,EAC7B,OAAe,EACf,KAAK,GAAG,EAAE;IAEV,MAAM,OAAO,GAAsB,EAAE,CAAC;IAEtC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,GAAG,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;QAC/E,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO;YAAE,SAAS;QAEvB,qEAAqE;QACrE,MAAM,QAAQ,GAAG,QAAQ;aACtB,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC;aACvB,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;aAC9B,IAAI,CAAC,GAAG,CAAC,CAAC;QAEb,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC;QACtB,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,wEAAwE;QACxE,yEAAyE;QACzE,yEAAyE;QACzE,IAAI,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM;YAAE,SAAS;QAEhD,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -15,15 +15,14 @@ export declare const transcriptSegmentSchema: z.ZodObject<{
|
|
|
15
15
|
text: z.ZodString;
|
|
16
16
|
}, "strip", z.ZodTypeAny, {
|
|
17
17
|
text: string;
|
|
18
|
-
start: number;
|
|
19
18
|
end: number;
|
|
19
|
+
start: number;
|
|
20
20
|
}, {
|
|
21
21
|
text: string;
|
|
22
|
-
start: number;
|
|
23
22
|
end: number;
|
|
23
|
+
start: number;
|
|
24
24
|
}>;
|
|
25
25
|
export type TranscriptSegment = z.infer<typeof transcriptSegmentSchema>;
|
|
26
|
-
export type TranscriptFormat = 'text' | 'timestamped' | 'segments';
|
|
27
26
|
/** Bumped whenever the cache representation changes, so old files are refetched. */
|
|
28
27
|
export declare const TRANSCRIPT_CACHE_VERSION = 2;
|
|
29
28
|
/**
|
|
@@ -47,39 +46,37 @@ export declare const cachedTranscriptSchema: z.ZodObject<{
|
|
|
47
46
|
text: z.ZodString;
|
|
48
47
|
}, "strip", z.ZodTypeAny, {
|
|
49
48
|
text: string;
|
|
50
|
-
start: number;
|
|
51
49
|
end: number;
|
|
50
|
+
start: number;
|
|
52
51
|
}, {
|
|
53
52
|
text: string;
|
|
54
|
-
start: number;
|
|
55
53
|
end: number;
|
|
54
|
+
start: number;
|
|
56
55
|
}>, "many">;
|
|
57
56
|
}, "strip", z.ZodTypeAny, {
|
|
58
57
|
videoId: string;
|
|
59
58
|
version: number;
|
|
60
59
|
language: string;
|
|
60
|
+
fetchedAt: string;
|
|
61
61
|
segments: {
|
|
62
62
|
text: string;
|
|
63
|
-
start: number;
|
|
64
63
|
end: number;
|
|
64
|
+
start: number;
|
|
65
65
|
}[];
|
|
66
|
-
fetchedAt: string;
|
|
67
66
|
}, {
|
|
68
67
|
videoId: string;
|
|
69
68
|
version: number;
|
|
70
69
|
language: string;
|
|
70
|
+
fetchedAt: string;
|
|
71
71
|
segments: {
|
|
72
72
|
text: string;
|
|
73
|
-
start: number;
|
|
74
73
|
end: number;
|
|
74
|
+
start: number;
|
|
75
75
|
}[];
|
|
76
|
-
fetchedAt: string;
|
|
77
76
|
}>;
|
|
78
77
|
export type CachedTranscript = z.infer<typeof cachedTranscriptSchema>;
|
|
79
78
|
/** 3723.5 -> "1:02:03", 62 -> "1:02" */
|
|
80
79
|
export declare function formatTimestamp(totalSeconds: number): string;
|
|
81
|
-
/** 3723.5 -> "01:02:03,500" — the SRT wire format. */
|
|
82
|
-
export declare function formatSrtTimestamp(totalSeconds: number): string;
|
|
83
80
|
/**
|
|
84
81
|
* Parse WebVTT into segments.
|
|
85
82
|
*
|
|
@@ -105,24 +102,6 @@ export declare function sliceSegments(segments: TranscriptSegment[], range: {
|
|
|
105
102
|
startTime?: number;
|
|
106
103
|
endTime?: number;
|
|
107
104
|
}): TranscriptSegment[];
|
|
108
|
-
export interface TranscriptMatch {
|
|
109
|
-
segment: TranscriptSegment;
|
|
110
|
-
/** The matched text, for highlighting. */
|
|
111
|
-
match: string;
|
|
112
|
-
url: string;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Find a phrase or pattern in a transcript.
|
|
116
|
-
*
|
|
117
|
-
* Matching runs over the joined text of a small window of segments, not each
|
|
118
|
-
* segment alone, so a phrase split across a caption boundary is still found —
|
|
119
|
-
* which is most phrases, since caption breaks fall mid-sentence.
|
|
120
|
-
*/
|
|
121
|
-
export declare function searchSegments(segments: TranscriptSegment[], query: string, options?: {
|
|
122
|
-
regex?: boolean;
|
|
123
|
-
caseSensitive?: boolean;
|
|
124
|
-
limit?: number;
|
|
125
|
-
}): TranscriptMatch[];
|
|
126
105
|
export interface TruncationResult {
|
|
127
106
|
text: string;
|
|
128
107
|
truncated: boolean;
|
|
@@ -138,6 +117,6 @@ export interface TruncationResult {
|
|
|
138
117
|
* continue from.
|
|
139
118
|
*/
|
|
140
119
|
export declare function windowText(text: string, offset?: number, maxChars?: number): TruncationResult;
|
|
141
|
-
export
|
|
142
|
-
export
|
|
120
|
+
export * from './subtitles.js';
|
|
121
|
+
export * from './transcript-search.js';
|
|
143
122
|
//# sourceMappingURL=transcript.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../src/utils/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;GAQG;AAEH,eAAO,MAAM,uBAAuB;IAClC,2CAA2C;;;;;;;;;;;;EAI3C,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,
|
|
1
|
+
{"version":3,"file":"transcript.d.ts","sourceRoot":"","sources":["../../src/utils/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;GAQG;AAEH,eAAO,MAAM,uBAAuB;IAClC,2CAA2C;;;;;;;;;;;;EAI3C,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB;;;;;;QApBjC,2CAA2C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0B3C,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,wCAAwC;AACxC,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAS5D;AA0BD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,EAAE,CA4BhE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAMpE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAI3E;AAED,iDAAiD;AACjD,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,KAAK,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9C,iBAAiB,EAAE,CAOrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,SAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAcxF;AAED,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC"}
|
package/dist/utils/transcript.js
CHANGED
|
@@ -44,16 +44,6 @@ export function formatTimestamp(totalSeconds) {
|
|
|
44
44
|
return `${hours}:${minutes.toString().padStart(2, '0')}:${paddedSeconds}`;
|
|
45
45
|
return `${minutes}:${paddedSeconds}`;
|
|
46
46
|
}
|
|
47
|
-
/** 3723.5 -> "01:02:03,500" — the SRT wire format. */
|
|
48
|
-
export function formatSrtTimestamp(totalSeconds) {
|
|
49
|
-
const clamped = Math.max(0, totalSeconds);
|
|
50
|
-
const hours = Math.floor(clamped / 3600);
|
|
51
|
-
const minutes = Math.floor((clamped % 3600) / 60);
|
|
52
|
-
const seconds = Math.floor(clamped % 60);
|
|
53
|
-
const millis = Math.round((clamped - Math.floor(clamped)) * 1000);
|
|
54
|
-
const pad = (value, width = 2) => value.toString().padStart(width, '0');
|
|
55
|
-
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)},${pad(millis, 3)}`;
|
|
56
|
-
}
|
|
57
47
|
/**
|
|
58
48
|
* WebVTT cue text is a small markup language: inline timestamps and <c> spans
|
|
59
49
|
* for karaoke-style highlighting. The parser hands back the spec's parsed tree,
|
|
@@ -143,42 +133,6 @@ export function sliceSegments(segments, range) {
|
|
|
143
133
|
// relevant to the range the caller asked about.
|
|
144
134
|
return segments.filter((segment) => segment.end > from && segment.start < to);
|
|
145
135
|
}
|
|
146
|
-
/**
|
|
147
|
-
* Find a phrase or pattern in a transcript.
|
|
148
|
-
*
|
|
149
|
-
* Matching runs over the joined text of a small window of segments, not each
|
|
150
|
-
* segment alone, so a phrase split across a caption boundary is still found —
|
|
151
|
-
* which is most phrases, since caption breaks fall mid-sentence.
|
|
152
|
-
*/
|
|
153
|
-
export function searchSegments(segments, query, options = {}) {
|
|
154
|
-
const { regex = false, caseSensitive = false, limit = 20 } = options;
|
|
155
|
-
const flags = caseSensitive ? 'g' : 'gi';
|
|
156
|
-
const pattern = regex
|
|
157
|
-
? new RegExp(query, flags)
|
|
158
|
-
: new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), flags);
|
|
159
|
-
const matches = [];
|
|
160
|
-
for (let index = 0; index < segments.length && matches.length < limit; index++) {
|
|
161
|
-
const current = segments[index];
|
|
162
|
-
if (!current)
|
|
163
|
-
continue;
|
|
164
|
-
// Two segments of lookahead covers phrases spanning a caption break.
|
|
165
|
-
const haystack = segments
|
|
166
|
-
.slice(index, index + 3)
|
|
167
|
-
.map((segment) => segment.text)
|
|
168
|
-
.join(' ');
|
|
169
|
-
pattern.lastIndex = 0;
|
|
170
|
-
const found = pattern.exec(haystack);
|
|
171
|
-
if (!found)
|
|
172
|
-
continue;
|
|
173
|
-
// The lookahead window means a hit can start in a *later* segment. Only
|
|
174
|
-
// accept it here if it begins inside this one; otherwise the same hit is
|
|
175
|
-
// reported once per preceding segment, and attributed to the wrong time.
|
|
176
|
-
if (found.index > current.text.length)
|
|
177
|
-
continue;
|
|
178
|
-
matches.push({ segment: current, match: found[0], url: '' });
|
|
179
|
-
}
|
|
180
|
-
return matches;
|
|
181
|
-
}
|
|
182
136
|
/**
|
|
183
137
|
* Window a long string.
|
|
184
138
|
*
|
|
@@ -198,22 +152,6 @@ export function windowText(text, offset = 0, maxChars) {
|
|
|
198
152
|
const end = softEnd > from ? softEnd : hardEnd;
|
|
199
153
|
return { text: text.slice(from, end), truncated: true, nextOffset: end, totalChars };
|
|
200
154
|
}
|
|
201
|
-
export
|
|
202
|
-
|
|
203
|
-
.map((segment, index) => [
|
|
204
|
-
index + 1,
|
|
205
|
-
`${formatSrtTimestamp(segment.start)} --> ${formatSrtTimestamp(segment.end)}`,
|
|
206
|
-
segment.text,
|
|
207
|
-
].join('\n'))
|
|
208
|
-
.join('\n\n') + '\n');
|
|
209
|
-
}
|
|
210
|
-
export function toVtt(segments) {
|
|
211
|
-
const body = segments
|
|
212
|
-
.map((segment) => [
|
|
213
|
-
`${formatSrtTimestamp(segment.start).replace(',', '.')} --> ${formatSrtTimestamp(segment.end).replace(',', '.')}`,
|
|
214
|
-
segment.text,
|
|
215
|
-
].join('\n'))
|
|
216
|
-
.join('\n\n');
|
|
217
|
-
return `WEBVTT\n\n${body}\n`;
|
|
218
|
-
}
|
|
155
|
+
export * from './subtitles.js';
|
|
156
|
+
export * from './transcript-search.js';
|
|
219
157
|
//# sourceMappingURL=transcript.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../src/utils/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,MAAM,MAAM,eAAe,CAAC;AAEnC;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,2CAA2C;IAC3C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;CACjB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../src/utils/transcript.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The identifiers and URLs this server is willing to hand to yt-dlp.
|
|
3
|
+
*
|
|
4
|
+
* A tool argument is model-controlled, and yt-dlp's generic extractor will
|
|
5
|
+
* fetch any URL it is given — including internal addresses — so anything that
|
|
6
|
+
* becomes a yt-dlp target is checked here first. Ids are also filenames, so the
|
|
7
|
+
* real formats are matched exactly rather than sanitised.
|
|
8
|
+
*/
|
|
9
|
+
/** YouTube video ids are 11 base64url characters. */
|
|
10
|
+
export declare const VIDEO_ID_PATTERN: RegExp;
|
|
11
|
+
export declare function assertVideoId(videoId: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* YouTube channel ids are `UC` followed by 22 base64url characters.
|
|
14
|
+
*
|
|
15
|
+
* This is stricter than it looks like it needs to be. A channel id arrives from
|
|
16
|
+
* yt-dlp's output and is then used as a directory name under
|
|
17
|
+
* `~/.youtube-knowledge/brains`, so a value containing a separator or `..`
|
|
18
|
+
* would write outside that tree. Matching the real format exactly is cheaper
|
|
19
|
+
* than sanitising a value that should never have been unusual.
|
|
20
|
+
*/
|
|
21
|
+
export declare const CHANNEL_ID_PATTERN: RegExp;
|
|
22
|
+
export declare function assertChannelId(channelId: string): string;
|
|
23
|
+
/** The hosts yt-dlp may be pointed at. Anything else is refused before it spawns. */
|
|
24
|
+
export declare const YOUTUBE_HOSTS: readonly ["youtube.com", "www.youtube.com", "m.youtube.com", "music.youtube.com", "youtu.be", "www.youtube-nocookie.com"];
|
|
25
|
+
/**
|
|
26
|
+
* Validate a caller-supplied URL and return it normalised to https.
|
|
27
|
+
*
|
|
28
|
+
* `http:` is upgraded rather than refused, and a URL written without a scheme
|
|
29
|
+
* but starting with a YouTube host is accepted, because both are things people
|
|
30
|
+
* paste. Everything else — other hosts, other schemes, credentials, ports — is
|
|
31
|
+
* an `INVALID_INPUT`, since the only reason to send one is to make yt-dlp fetch
|
|
32
|
+
* something that is not YouTube.
|
|
33
|
+
*/
|
|
34
|
+
export declare function assertYouTubeUrl(input: string): string;
|
|
35
|
+
//# sourceMappingURL=validate-youtube.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-youtube.d.ts","sourceRoot":"","sources":["../../src/utils/validate-youtube.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,qDAAqD;AACrD,eAAO,MAAM,gBAAgB,QAAwB,CAAC;AAEtD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOrD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,QAA0B,CAAC;AAE1D,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAYzD;AAED,qFAAqF;AACrF,eAAO,MAAM,aAAa,2HAOhB,CAAC;AAKX;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAqBtD"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { YouTubeError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* The identifiers and URLs this server is willing to hand to yt-dlp.
|
|
4
|
+
*
|
|
5
|
+
* A tool argument is model-controlled, and yt-dlp's generic extractor will
|
|
6
|
+
* fetch any URL it is given — including internal addresses — so anything that
|
|
7
|
+
* becomes a yt-dlp target is checked here first. Ids are also filenames, so the
|
|
8
|
+
* real formats are matched exactly rather than sanitised.
|
|
9
|
+
*/
|
|
10
|
+
/** YouTube video ids are 11 base64url characters. */
|
|
11
|
+
export const VIDEO_ID_PATTERN = /^[A-Za-z0-9_-]{11}$/;
|
|
12
|
+
export function assertVideoId(videoId) {
|
|
13
|
+
if (!VIDEO_ID_PATTERN.test(videoId)) {
|
|
14
|
+
throw new YouTubeError('INVALID_INPUT', `"${clip(videoId)}" is not a valid YouTube video id.`, {
|
|
15
|
+
nextStep: 'Video ids are 11 characters, for example dQw4w9WgXcQ.',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return videoId;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* YouTube channel ids are `UC` followed by 22 base64url characters.
|
|
22
|
+
*
|
|
23
|
+
* This is stricter than it looks like it needs to be. A channel id arrives from
|
|
24
|
+
* yt-dlp's output and is then used as a directory name under
|
|
25
|
+
* `~/.youtube-knowledge/brains`, so a value containing a separator or `..`
|
|
26
|
+
* would write outside that tree. Matching the real format exactly is cheaper
|
|
27
|
+
* than sanitising a value that should never have been unusual.
|
|
28
|
+
*/
|
|
29
|
+
export const CHANNEL_ID_PATTERN = /^UC[A-Za-z0-9_-]{22}$/;
|
|
30
|
+
export function assertChannelId(channelId) {
|
|
31
|
+
if (!CHANNEL_ID_PATTERN.test(channelId)) {
|
|
32
|
+
throw new YouTubeError('INVALID_INPUT', `"${clip(channelId)}" is not a valid YouTube channel id.`, {
|
|
33
|
+
nextStep: 'Channel ids look like UCxxxxxxxxxxxxxxxxxxxxxx. Call get_channel_info or list_brains to get one.',
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return channelId;
|
|
37
|
+
}
|
|
38
|
+
/** The hosts yt-dlp may be pointed at. Anything else is refused before it spawns. */
|
|
39
|
+
export const YOUTUBE_HOSTS = [
|
|
40
|
+
'youtube.com',
|
|
41
|
+
'www.youtube.com',
|
|
42
|
+
'm.youtube.com',
|
|
43
|
+
'music.youtube.com',
|
|
44
|
+
'youtu.be',
|
|
45
|
+
'www.youtube-nocookie.com',
|
|
46
|
+
];
|
|
47
|
+
const HOST_SET = new Set(YOUTUBE_HOSTS);
|
|
48
|
+
const SCHEMELESS_YOUTUBE = /^(?:www\.|m\.|music\.)?(?:youtube\.com|youtu\.be)\//i;
|
|
49
|
+
/**
|
|
50
|
+
* Validate a caller-supplied URL and return it normalised to https.
|
|
51
|
+
*
|
|
52
|
+
* `http:` is upgraded rather than refused, and a URL written without a scheme
|
|
53
|
+
* but starting with a YouTube host is accepted, because both are things people
|
|
54
|
+
* paste. Everything else — other hosts, other schemes, credentials, ports — is
|
|
55
|
+
* an `INVALID_INPUT`, since the only reason to send one is to make yt-dlp fetch
|
|
56
|
+
* something that is not YouTube.
|
|
57
|
+
*/
|
|
58
|
+
export function assertYouTubeUrl(input) {
|
|
59
|
+
const trimmed = input.trim();
|
|
60
|
+
const candidate = SCHEMELESS_YOUTUBE.test(trimmed) ? `https://${trimmed}` : trimmed;
|
|
61
|
+
let url;
|
|
62
|
+
try {
|
|
63
|
+
url = new URL(candidate);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
throw notYouTube(input);
|
|
67
|
+
}
|
|
68
|
+
if (url.protocol === 'http:')
|
|
69
|
+
url.protocol = 'https:';
|
|
70
|
+
const wrongScheme = url.protocol !== 'https:';
|
|
71
|
+
const wrongHost = !HOST_SET.has(url.hostname.toLowerCase());
|
|
72
|
+
const hasCredentials = url.username !== '' || url.password !== '';
|
|
73
|
+
const hasPort = url.port !== '';
|
|
74
|
+
if (wrongScheme || wrongHost || hasCredentials || hasPort)
|
|
75
|
+
throw notYouTube(input);
|
|
76
|
+
return url.toString();
|
|
77
|
+
}
|
|
78
|
+
function notYouTube(input) {
|
|
79
|
+
return new YouTubeError('INVALID_INPUT', `"${clip(input)}" is not a YouTube URL.`, {
|
|
80
|
+
nextStep: `Pass an https URL on one of: ${YOUTUBE_HOSTS.join(', ')}.`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** Keeps a rejected value from flooding the error message it is quoted in. */
|
|
84
|
+
function clip(value) {
|
|
85
|
+
return value.length > 64 ? `${value.slice(0, 61)}...` : value;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=validate-youtube.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-youtube.js","sourceRoot":"","sources":["../../src/utils/validate-youtube.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/validate.d.ts
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Input validation for the values that reach the filesystem or yt-dlp's
|
|
3
|
-
* language negotiation.
|
|
3
|
+
* language negotiation. The YouTube-specific checks — ids and URLs — live in
|
|
4
|
+
* validate-youtube.ts and are re-exported here. Zod already constrains shapes at the tool boundary;
|
|
4
5
|
* these are the checks Zod cannot express.
|
|
5
6
|
*/
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
+
* Prove that a caller-supplied path lies inside the home directory.
|
|
8
9
|
*
|
|
9
10
|
* A tool argument is model-controlled, so it must not be able to write outside
|
|
10
|
-
* the user's home directory or reach the filesystem root through `..`.
|
|
11
|
+
* the user's home directory or reach the filesystem root through `..`. The
|
|
12
|
+
* check is made on the real path: a symlink inside home that points outside it
|
|
13
|
+
* passed a purely lexical comparison, and yt-dlp and ffmpeg then wrote through
|
|
14
|
+
* it. The nearest existing ancestor is resolved and the missing tail re-joined,
|
|
15
|
+
* so a directory that does not exist yet is judged by where it would be created.
|
|
11
16
|
*/
|
|
17
|
+
export declare function assertInsideHome(input: string, what: string): string;
|
|
18
|
+
/** Resolve a caller-supplied output directory, or the fallback when none is given. */
|
|
12
19
|
export declare function resolveOutputDir(outputDir: string | undefined, fallback: string): string;
|
|
13
20
|
export declare function assertLanguageTag(language: string): string;
|
|
14
|
-
export declare function assertChannelId(channelId: string): string;
|
|
15
21
|
/** Accepts `90`, `1:30`, `01:02:03`, `1:30.5` and returns seconds. */
|
|
16
22
|
export declare function parseTimestamp(value: string | number, field: string): number;
|
|
23
|
+
export * from './validate-youtube.js';
|
|
17
24
|
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAqBpE;AAkBD,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxF;AAKD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO1D;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAoB5E;AAED,cAAc,uBAAuB,CAAC"}
|