youtube-knowledge-mcp 2.2.0 → 3.0.1
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 +126 -36
- package/dist/brain-schemas.d.ts +10 -0
- package/dist/brain-schemas.d.ts.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 +111 -0
- package/dist/harvest-schemas.js.map +1 -0
- package/dist/registry/brain-tools.d.ts.map +1 -1
- package/dist/registry/brain-tools.js +16 -0
- package/dist/registry/brain-tools.js.map +1 -1
- package/dist/registry/channel-tools.d.ts.map +1 -1
- package/dist/registry/channel-tools.js +16 -0
- package/dist/registry/channel-tools.js.map +1 -1
- 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.map +1 -1
- package/dist/registry/health-tools.js +18 -0
- package/dist/registry/health-tools.js.map +1 -1
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +6 -0
- package/dist/registry/index.js.map +1 -1
- package/dist/registry/media-tools.d.ts.map +1 -1
- package/dist/registry/media-tools.js +31 -0
- package/dist/registry/media-tools.js.map +1 -1
- package/dist/registry/store-tools.d.ts +9 -0
- package/dist/registry/store-tools.d.ts.map +1 -0
- package/dist/registry/store-tools.js +59 -0
- package/dist/registry/store-tools.js.map +1 -0
- package/dist/registry/transcript-tools.d.ts +4 -1
- package/dist/registry/transcript-tools.d.ts.map +1 -1
- package/dist/registry/transcript-tools.js +20 -1
- package/dist/registry/transcript-tools.js.map +1 -1
- package/dist/registry/video-tools.d.ts.map +1 -1
- package/dist/registry/video-tools.js +49 -1
- package/dist/registry/video-tools.js.map +1 -1
- package/dist/registry/vision-tools.d.ts +7 -0
- package/dist/registry/vision-tools.d.ts.map +1 -0
- package/dist/registry/vision-tools.js +55 -0
- package/dist/registry/vision-tools.js.map +1 -0
- package/dist/schemas.d.ts +245 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +58 -0
- package/dist/schemas.js.map +1 -1
- package/dist/thumbnail-schemas.d.ts +38 -28
- package/dist/thumbnail-schemas.d.ts.map +1 -1
- package/dist/tools/ask-brain.js +1 -1
- package/dist/tools/ask-brain.js.map +1 -1
- package/dist/tools/ask-brains.d.ts +66 -0
- package/dist/tools/ask-brains.d.ts.map +1 -0
- package/dist/tools/ask-brains.js +111 -0
- package/dist/tools/ask-brains.js.map +1 -0
- package/dist/tools/brain-info.js +1 -1
- package/dist/tools/brain-info.js.map +1 -1
- package/dist/tools/check-health.d.ts +75 -6
- package/dist/tools/check-health.d.ts.map +1 -1
- package/dist/tools/check-health.js +114 -6
- package/dist/tools/check-health.js.map +1 -1
- package/dist/tools/check-new-uploads.d.ts +55 -0
- package/dist/tools/check-new-uploads.d.ts.map +1 -0
- package/dist/tools/check-new-uploads.js +74 -0
- package/dist/tools/check-new-uploads.js.map +1 -0
- package/dist/tools/clips.d.ts +15 -0
- package/dist/tools/clips.d.ts.map +1 -1
- package/dist/tools/clips.js +5 -5
- package/dist/tools/clips.js.map +1 -1
- package/dist/tools/detect-scenes.d.ts +38 -0
- package/dist/tools/detect-scenes.d.ts.map +1 -0
- package/dist/tools/detect-scenes.js +66 -0
- package/dist/tools/detect-scenes.js.map +1 -0
- package/dist/tools/export-data.d.ts +85 -0
- package/dist/tools/export-data.d.ts.map +1 -0
- package/dist/tools/export-data.js +99 -0
- package/dist/tools/export-data.js.map +1 -0
- package/dist/tools/fetch-channel-thumbnails.d.ts +8 -8
- package/dist/tools/fetch-videos.d.ts +15 -4
- package/dist/tools/fetch-videos.d.ts.map +1 -1
- package/dist/tools/fetch-videos.js +63 -22
- package/dist/tools/fetch-videos.js.map +1 -1
- package/dist/tools/frames-subtitles.d.ts.map +1 -1
- package/dist/tools/frames-subtitles.js +16 -8
- package/dist/tools/frames-subtitles.js.map +1 -1
- package/dist/tools/frames.d.ts +73 -0
- package/dist/tools/frames.d.ts.map +1 -0
- package/dist/tools/frames.js +136 -0
- package/dist/tools/frames.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 +4 -0
- 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-sponsor-segments.d.ts +49 -0
- package/dist/tools/get-sponsor-segments.d.ts.map +1 -0
- package/dist/tools/get-sponsor-segments.js +61 -0
- package/dist/tools/get-sponsor-segments.js.map +1 -0
- package/dist/tools/get-thumbnail.d.ts.map +1 -1
- package/dist/tools/get-thumbnail.js +2 -1
- package/dist/tools/get-thumbnail.js.map +1 -1
- package/dist/tools/get-transcript.d.ts +1 -0
- package/dist/tools/get-transcript.d.ts.map +1 -1
- package/dist/tools/get-transcript.js +11 -4
- package/dist/tools/get-transcript.js.map +1 -1
- package/dist/tools/get-video-details.d.ts +118 -0
- package/dist/tools/get-video-details.d.ts.map +1 -0
- package/dist/tools/get-video-details.js +147 -0
- package/dist/tools/get-video-details.js.map +1 -0
- package/dist/tools/gif-concat.d.ts +49 -0
- package/dist/tools/gif-concat.d.ts.map +1 -0
- package/dist/tools/gif-concat.js +96 -0
- package/dist/tools/gif-concat.js.map +1 -0
- 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/harvest-details.d.ts +84 -0
- package/dist/tools/harvest-details.d.ts.map +1 -0
- package/dist/tools/harvest-details.js +71 -0
- package/dist/tools/harvest-details.js.map +1 -0
- package/dist/tools/library.js +1 -1
- package/dist/tools/library.js.map +1 -1
- package/dist/tools/list-captions.d.ts +17 -0
- package/dist/tools/list-captions.d.ts.map +1 -0
- package/dist/tools/list-captions.js +60 -0
- package/dist/tools/list-captions.js.map +1 -0
- package/dist/tools/list-channel-playlists.d.ts +30 -0
- package/dist/tools/list-channel-playlists.d.ts.map +1 -0
- package/dist/tools/list-channel-playlists.js +44 -0
- package/dist/tools/list-channel-playlists.js.map +1 -0
- package/dist/tools/list-channel-thumbnails.d.ts +10 -10
- package/dist/tools/list-channel-thumbnails.js +1 -1
- package/dist/tools/list-channel-thumbnails.js.map +1 -1
- 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/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-videos.js +1 -1
- package/dist/tools/search-videos.js.map +1 -1
- package/dist/tools/transcribe-video.d.ts +28 -0
- package/dist/tools/transcribe-video.d.ts.map +1 -0
- package/dist/tools/transcribe-video.js +52 -0
- package/dist/tools/transcribe-video.js.map +1 -0
- package/dist/utils/brain-fusion.d.ts +24 -0
- package/dist/utils/brain-fusion.d.ts.map +1 -0
- package/dist/utils/brain-fusion.js +18 -0
- package/dist/utils/brain-fusion.js.map +1 -0
- package/dist/utils/channel-playlists.d.ts +21 -0
- package/dist/utils/channel-playlists.d.ts.map +1 -0
- package/dist/utils/channel-playlists.js +56 -0
- package/dist/utils/channel-playlists.js.map +1 -0
- package/dist/utils/clips.d.ts.map +1 -1
- package/dist/utils/clips.js +3 -17
- 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/concat.d.ts +19 -0
- package/dist/utils/concat.d.ts.map +1 -0
- package/dist/utils/concat.js +135 -0
- package/dist/utils/concat.js.map +1 -0
- package/dist/utils/contact-sheet.d.ts +28 -0
- package/dist/utils/contact-sheet.d.ts.map +1 -0
- package/dist/utils/contact-sheet.js +65 -0
- package/dist/utils/contact-sheet.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/detail-batch.d.ts +25 -0
- package/dist/utils/detail-batch.d.ts.map +1 -0
- package/dist/utils/detail-batch.js +58 -0
- package/dist/utils/detail-batch.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 +19 -1
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/export-data.d.ts +41 -0
- package/dist/utils/export-data.d.ts.map +1 -0
- package/dist/utils/export-data.js +114 -0
- package/dist/utils/export-data.js.map +1 -0
- package/dist/utils/export-format.d.ts +23 -0
- package/dist/utils/export-format.d.ts.map +1 -0
- package/dist/utils/export-format.js +59 -0
- package/dist/utils/export-format.js.map +1 -0
- package/dist/utils/export-load.d.ts +28 -0
- package/dist/utils/export-load.d.ts.map +1 -0
- package/dist/utils/export-load.js +70 -0
- package/dist/utils/export-load.js.map +1 -0
- package/dist/utils/export-rows.d.ts +35 -0
- package/dist/utils/export-rows.d.ts.map +1 -0
- package/dist/utils/export-rows.js +69 -0
- package/dist/utils/export-rows.js.map +1 -0
- package/dist/utils/ffmpeg.d.ts +26 -0
- package/dist/utils/ffmpeg.d.ts.map +1 -0
- package/dist/utils/ffmpeg.js +28 -0
- package/dist/utils/ffmpeg.js.map +1 -0
- package/dist/utils/format.d.ts +23 -1
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +5 -1
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/frame-plan.d.ts +24 -0
- package/dist/utils/frame-plan.d.ts.map +1 -0
- package/dist/utils/frame-plan.js +58 -0
- package/dist/utils/frame-plan.js.map +1 -0
- package/dist/utils/frames.d.ts +32 -0
- package/dist/utils/frames.d.ts.map +1 -0
- package/dist/utils/frames.js +60 -0
- package/dist/utils/frames.js.map +1 -0
- package/dist/utils/gif.d.ts +31 -0
- package/dist/utils/gif.d.ts.map +1 -0
- package/dist/utils/gif.js +77 -0
- package/dist/utils/gif.js.map +1 -0
- package/dist/utils/harvest-catalog.d.ts +54 -0
- package/dist/utils/harvest-catalog.d.ts.map +1 -0
- package/dist/utils/harvest-catalog.js +173 -0
- package/dist/utils/harvest-catalog.js.map +1 -0
- package/dist/utils/harvest-details.d.ts +30 -0
- package/dist/utils/harvest-details.d.ts.map +1 -0
- package/dist/utils/harvest-details.js +98 -0
- package/dist/utils/harvest-details.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/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/local-transcription.d.ts +14 -0
- package/dist/utils/local-transcription.d.ts.map +1 -0
- package/dist/utils/local-transcription.js +85 -0
- package/dist/utils/local-transcription.js.map +1 -0
- package/dist/utils/moment.d.ts +24 -0
- package/dist/utils/moment.d.ts.map +1 -0
- package/dist/utils/moment.js +22 -0
- package/dist/utils/moment.js.map +1 -0
- package/dist/utils/most-replayed.d.ts +15 -0
- package/dist/utils/most-replayed.d.ts.map +1 -0
- package/dist/utils/most-replayed.js +25 -0
- package/dist/utils/most-replayed.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/scenes.d.ts +39 -0
- package/dist/utils/scenes.d.ts.map +1 -0
- package/dist/utils/scenes.js +83 -0
- package/dist/utils/scenes.js.map +1 -0
- package/dist/utils/seconds.d.ts +5 -0
- package/dist/utils/seconds.d.ts.map +1 -0
- package/dist/utils/seconds.js +7 -0
- package/dist/utils/seconds.js.map +1 -0
- 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/sponsorblock.d.ts +24 -0
- package/dist/utils/sponsorblock.d.ts.map +1 -0
- package/dist/utils/sponsorblock.js +153 -0
- package/dist/utils/sponsorblock.js.map +1 -0
- 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/transcript-cache.d.ts +5 -1
- package/dist/utils/transcript-cache.d.ts.map +1 -1
- package/dist/utils/transcript-cache.js +31 -50
- package/dist/utils/transcript-cache.js.map +1 -1
- package/dist/utils/transcript-files.d.ts +24 -0
- package/dist/utils/transcript-files.d.ts.map +1 -0
- package/dist/utils/transcript-files.js +61 -0
- package/dist/utils/transcript-files.js.map +1 -0
- package/dist/utils/transcript.d.ts +3 -0
- package/dist/utils/transcript.d.ts.map +1 -1
- package/dist/utils/transcript.js +2 -0
- package/dist/utils/transcript.js.map +1 -1
- package/dist/utils/upload-check.d.ts +38 -0
- package/dist/utils/upload-check.d.ts.map +1 -0
- package/dist/utils/upload-check.js +93 -0
- package/dist/utils/upload-check.js.map +1 -0
- package/dist/utils/validate.d.ts +2 -0
- package/dist/utils/validate.d.ts.map +1 -1
- package/dist/utils/validate.js +4 -0
- package/dist/utils/validate.js.map +1 -1
- package/dist/utils/video-detail-store.d.ts +47 -0
- package/dist/utils/video-detail-store.d.ts.map +1 -0
- package/dist/utils/video-detail-store.js +89 -0
- package/dist/utils/video-detail-store.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-schema.d.ts +187 -0
- package/dist/utils/video-stats-schema.d.ts.map +1 -0
- package/dist/utils/video-stats-schema.js +50 -0
- package/dist/utils/video-stats-schema.js.map +1 -0
- package/dist/utils/video-stats-storage.d.ts +16 -0
- package/dist/utils/video-stats-storage.d.ts.map +1 -0
- package/dist/utils/video-stats-storage.js +37 -0
- package/dist/utils/video-stats-storage.js.map +1 -0
- package/dist/utils/video-stats.d.ts +82 -0
- package/dist/utils/video-stats.d.ts.map +1 -0
- package/dist/utils/video-stats.js +108 -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/video-stream.d.ts +21 -0
- package/dist/utils/video-stream.d.ts.map +1 -0
- package/dist/utils/video-stream.js +36 -0
- package/dist/utils/video-stream.js.map +1 -0
- package/dist/utils/whisper.d.ts +39 -0
- package/dist/utils/whisper.d.ts.map +1 -0
- package/dist/utils/whisper.js +137 -0
- package/dist/utils/whisper.js.map +1 -0
- package/dist/utils/youtube-channel.d.ts +13 -2
- package/dist/utils/youtube-channel.d.ts.map +1 -1
- package/dist/utils/youtube-channel.js +39 -3
- package/dist/utils/youtube-channel.js.map +1 -1
- package/dist/utils/youtube-schemas.d.ts +104 -0
- package/dist/utils/youtube-schemas.d.ts.map +1 -1
- package/dist/utils/youtube-schemas.js +32 -0
- package/dist/utils/youtube-schemas.js.map +1 -1
- package/dist/utils/youtube-video.d.ts +22 -1
- package/dist/utils/youtube-video.d.ts.map +1 -1
- package/dist/utils/youtube-video.js +40 -28
- package/dist/utils/youtube-video.js.map +1 -1
- package/dist/utils/ytdlp-limiter.d.ts +3 -0
- package/dist/utils/ytdlp-limiter.d.ts.map +1 -1
- package/dist/utils/ytdlp-limiter.js +19 -3
- package/dist/utils/ytdlp-limiter.js.map +1 -1
- 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-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 +10 -0
- package/dist/utils/ytdlp.d.ts.map +1 -1
- package/dist/utils/ytdlp.js +36 -25
- package/dist/utils/ytdlp.js.map +1 -1
- package/package.json +10 -4
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Writing rows out as CSV and Markdown tables.
|
|
3
|
+
*
|
|
4
|
+
* Exported comments are text written by strangers, and a spreadsheet will run a cell
|
|
5
|
+
* that begins like a formula. Every text field is therefore defused on the way out,
|
|
6
|
+
* as OWASP's CSV-injection guidance prescribes, rather than trusted because it came
|
|
7
|
+
* from a platform.
|
|
8
|
+
*/
|
|
9
|
+
/** Characters that make a spreadsheet read a cell as a formula (or as a command). */
|
|
10
|
+
const FORMULA_TRIGGERS = new Set(['=', '+', '-', '@', '\t', '\r']);
|
|
11
|
+
function defused(text) {
|
|
12
|
+
const first = text[0];
|
|
13
|
+
return first !== undefined && FORMULA_TRIGGERS.has(first) ? `'${text}` : text;
|
|
14
|
+
}
|
|
15
|
+
function csvField(cell, verbatim = false) {
|
|
16
|
+
if (cell === null || cell === undefined)
|
|
17
|
+
return '';
|
|
18
|
+
// Numbers and booleans are not text: a negative number is data, not a formula.
|
|
19
|
+
if (typeof cell !== 'string')
|
|
20
|
+
return String(cell);
|
|
21
|
+
const text = verbatim ? cell : defused(cell);
|
|
22
|
+
return /[",\r\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
|
23
|
+
}
|
|
24
|
+
/** RFC 4180: a header row, CRLF line endings, every record ended with one. */
|
|
25
|
+
export function toCsv(columns, rows) {
|
|
26
|
+
const header = columns.map((column) => csvField(column.header)).join(',');
|
|
27
|
+
const body = rows.map((row) => columns.map((column) => csvField(column.value(row), column.verbatim)).join(','));
|
|
28
|
+
return [header, ...body].map((line) => `${line}\r\n`).join('');
|
|
29
|
+
}
|
|
30
|
+
/** Characters that carry meaning in Markdown or HTML, and so must not be left to the reader's renderer. */
|
|
31
|
+
const MARKDOWN_ESCAPES = {
|
|
32
|
+
'\\': '\\\\',
|
|
33
|
+
'|': '\\|',
|
|
34
|
+
'`': '\\`',
|
|
35
|
+
'[': '\\[',
|
|
36
|
+
']': '\\]',
|
|
37
|
+
'<': '<',
|
|
38
|
+
'>': '>',
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* One table cell. Comment text is written by strangers and may hold HTML or links; it is
|
|
42
|
+
* shown as text, never as markup, and cannot end its cell or its row.
|
|
43
|
+
*/
|
|
44
|
+
function markdownCell(cell) {
|
|
45
|
+
if (cell === null || cell === undefined)
|
|
46
|
+
return '';
|
|
47
|
+
return String(cell)
|
|
48
|
+
.replace(/[\\|`[\]<>]/g, (character) => MARKDOWN_ESCAPES[character] ?? character)
|
|
49
|
+
.replace(/\r\n|\r|\n/g, ' ');
|
|
50
|
+
}
|
|
51
|
+
export function toMarkdownTable(columns, rows) {
|
|
52
|
+
const line = (cells) => `| ${cells.join(' | ')} |`;
|
|
53
|
+
return [
|
|
54
|
+
line(columns.map((column) => markdownCell(column.header))),
|
|
55
|
+
line(columns.map(() => '---')),
|
|
56
|
+
...rows.map((row) => line(columns.map((column) => markdownCell(column.value(row))))),
|
|
57
|
+
].join('\n');
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=export-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-format.js","sourceRoot":"","sources":["../../src/utils/export-format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import type { Coverage } from '../harvest-schemas.js';
|
|
3
|
+
import { type CommentRow, type VideoRow } from './export-rows.js';
|
|
4
|
+
/** What is loaded: the part of an export request that decides which rows. */
|
|
5
|
+
export interface LoadRequest {
|
|
6
|
+
channel?: string;
|
|
7
|
+
video?: string;
|
|
8
|
+
limit: number;
|
|
9
|
+
}
|
|
10
|
+
export interface Receipts {
|
|
11
|
+
coverage: Coverage[];
|
|
12
|
+
withoutReceipt: string[];
|
|
13
|
+
omittedReceipts: number;
|
|
14
|
+
}
|
|
15
|
+
export type Loaded = {
|
|
16
|
+
dataset: 'videos';
|
|
17
|
+
rows: VideoRow[];
|
|
18
|
+
total: number;
|
|
19
|
+
receipts: Receipts;
|
|
20
|
+
} | {
|
|
21
|
+
dataset: 'comments';
|
|
22
|
+
rows: CommentRow[];
|
|
23
|
+
total: number;
|
|
24
|
+
receipts: Receipts;
|
|
25
|
+
};
|
|
26
|
+
export declare function loadVideos(database: DatabaseSync, request: LoadRequest): Loaded;
|
|
27
|
+
export declare function loadComments(database: DatabaseSync, request: LoadRequest): Loaded;
|
|
28
|
+
//# sourceMappingURL=export-load.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-load.d.ts","sourceRoot":"","sources":["../../src/utils/export-load.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAA4B,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAU5F,6EAA6E;AAC7E,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,MAAM,GACd;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAC1E;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,UAAU,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC;AAsCnF,wBAAgB,UAAU,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,CAgB/E;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,CAiBjF"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { countComments, queryComments } from './comment-store.js';
|
|
2
|
+
import { COVERAGE_IN_RESULT_LIMIT } from './coverage.js';
|
|
3
|
+
import { toCommentRow, toVideoRow } from './export-rows.js';
|
|
4
|
+
import { readReceipt } from './harvest-receipts.js';
|
|
5
|
+
import { countVideos, queryVideos } from './video-store.js';
|
|
6
|
+
/**
|
|
7
|
+
* Reading the rows an export holds out of the store, and the receipts that say how whole they are.
|
|
8
|
+
*/
|
|
9
|
+
const PAGE = 1_000;
|
|
10
|
+
/** Pages through a query until `limit` rows, so memory holds rows and not a second copy of them. */
|
|
11
|
+
function collect(limit, page) {
|
|
12
|
+
const rows = [];
|
|
13
|
+
while (rows.length < limit) {
|
|
14
|
+
const got = page(rows.length, Math.min(PAGE, limit - rows.length));
|
|
15
|
+
rows.push(...got);
|
|
16
|
+
if (got.length === 0)
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
return rows;
|
|
20
|
+
}
|
|
21
|
+
function unique(values) {
|
|
22
|
+
return [...new Set(values.filter((value) => value !== undefined))];
|
|
23
|
+
}
|
|
24
|
+
/** The receipts for the targets in these rows, up to what a result carries. */
|
|
25
|
+
function receiptsFor(database, targets) {
|
|
26
|
+
const coverage = [];
|
|
27
|
+
const withoutReceipt = [];
|
|
28
|
+
let omittedReceipts = 0;
|
|
29
|
+
for (const { scope, ids, required } of targets) {
|
|
30
|
+
for (const id of ids.slice(0, COVERAGE_IN_RESULT_LIMIT)) {
|
|
31
|
+
const receipt = readReceipt(database, scope, id);
|
|
32
|
+
if (receipt !== undefined)
|
|
33
|
+
coverage.push(receipt);
|
|
34
|
+
else if (required)
|
|
35
|
+
withoutReceipt.push(`${scope}:${id}`);
|
|
36
|
+
}
|
|
37
|
+
omittedReceipts += Math.max(0, ids.length - COVERAGE_IN_RESULT_LIMIT);
|
|
38
|
+
}
|
|
39
|
+
return { coverage, withoutReceipt, omittedReceipts };
|
|
40
|
+
}
|
|
41
|
+
export function loadVideos(database, request) {
|
|
42
|
+
const filter = request.channel === undefined ? {} : { channelId: request.channel };
|
|
43
|
+
const rows = collect(request.limit, (offset, size) => queryVideos(database, { ...filter, order: 'catalog', limit: size, offset }).map(toVideoRow));
|
|
44
|
+
const channels = unique([request.channel, ...rows.map((row) => row.channelId)]);
|
|
45
|
+
return {
|
|
46
|
+
dataset: 'videos',
|
|
47
|
+
rows,
|
|
48
|
+
total: countVideos(database, filter),
|
|
49
|
+
receipts: receiptsFor(database, [
|
|
50
|
+
{ scope: 'channel-catalog', ids: channels, required: true },
|
|
51
|
+
{ scope: 'video-details', ids: channels, required: false },
|
|
52
|
+
]),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function loadComments(database, request) {
|
|
56
|
+
const filter = {
|
|
57
|
+
...(request.video === undefined ? {} : { videoId: request.video }),
|
|
58
|
+
...(request.channel === undefined ? {} : { channelId: request.channel }),
|
|
59
|
+
};
|
|
60
|
+
const rows = collect(request.limit, (offset, size) => queryComments(database, { ...filter, order: 'oldest', limit: size, offset }).map(toCommentRow));
|
|
61
|
+
return {
|
|
62
|
+
dataset: 'comments',
|
|
63
|
+
rows,
|
|
64
|
+
total: countComments(database, filter),
|
|
65
|
+
receipts: receiptsFor(database, [
|
|
66
|
+
{ scope: 'video-comments', ids: unique(rows.map((row) => row.videoId)), required: true },
|
|
67
|
+
]),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=export-load.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-load.js","sourceRoot":"","sources":["../../src/utils/export-load.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { StoredComment } from './comment-store.js';
|
|
2
|
+
import type { Column } from './export-format.js';
|
|
3
|
+
import type { StoredVideo } from './video-store.js';
|
|
4
|
+
/** The rows an export holds, as they are written, and the columns a CSV or table gives them. */
|
|
5
|
+
export interface VideoRow {
|
|
6
|
+
videoId: string;
|
|
7
|
+
channelId?: string;
|
|
8
|
+
title: string;
|
|
9
|
+
durationSeconds?: number;
|
|
10
|
+
uploadDate?: string;
|
|
11
|
+
viewCount?: number;
|
|
12
|
+
commentCount?: number;
|
|
13
|
+
tab?: string;
|
|
14
|
+
alsoInTabs: string[];
|
|
15
|
+
detailed: boolean;
|
|
16
|
+
storedComments: number;
|
|
17
|
+
}
|
|
18
|
+
export interface CommentRow {
|
|
19
|
+
commentId: string;
|
|
20
|
+
videoId: string;
|
|
21
|
+
parentId?: string;
|
|
22
|
+
author: string;
|
|
23
|
+
authorId?: string;
|
|
24
|
+
likeCount: number;
|
|
25
|
+
isPinned: boolean;
|
|
26
|
+
isUploader: boolean;
|
|
27
|
+
isVerified: boolean;
|
|
28
|
+
publishedAt?: string;
|
|
29
|
+
text: string;
|
|
30
|
+
}
|
|
31
|
+
export declare const VIDEO_COLUMNS: Column<VideoRow>[];
|
|
32
|
+
export declare const COMMENT_COLUMNS: Column<CommentRow>[];
|
|
33
|
+
export declare function toVideoRow(row: StoredVideo): VideoRow;
|
|
34
|
+
export declare function toCommentRow(row: StoredComment): CommentRow;
|
|
35
|
+
//# sourceMappingURL=export-rows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-rows.d.ts","sourceRoot":"","sources":["../../src/utils/export-rows.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,gGAAgG;AAEhG,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,QAAQ,CAAC,EAY3C,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,UAAU,CAAC,EAY/C,CAAC;AAWF,wBAAgB,UAAU,CAAC,GAAG,EAAE,WAAW,GAAG,QAAQ,CAcrD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,UAAU,CAgB3D"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const VIDEO_COLUMNS = [
|
|
3
|
+
{ header: 'videoId', value: (row) => row.videoId, verbatim: true },
|
|
4
|
+
{ header: 'channelId', value: (row) => row.channelId, verbatim: true },
|
|
5
|
+
{ header: 'title', value: (row) => row.title },
|
|
6
|
+
{ header: 'durationSeconds', value: (row) => row.durationSeconds },
|
|
7
|
+
{ header: 'uploadDate', value: (row) => row.uploadDate },
|
|
8
|
+
{ header: 'viewCount', value: (row) => row.viewCount },
|
|
9
|
+
{ header: 'commentCount', value: (row) => row.commentCount },
|
|
10
|
+
{ header: 'tab', value: (row) => row.tab },
|
|
11
|
+
{ header: 'alsoInTabs', value: (row) => row.alsoInTabs.join(' ') },
|
|
12
|
+
{ header: 'detailed', value: (row) => row.detailed },
|
|
13
|
+
{ header: 'storedComments', value: (row) => row.storedComments },
|
|
14
|
+
];
|
|
15
|
+
export const COMMENT_COLUMNS = [
|
|
16
|
+
{ header: 'commentId', value: (row) => row.commentId, verbatim: true },
|
|
17
|
+
{ header: 'videoId', value: (row) => row.videoId, verbatim: true },
|
|
18
|
+
{ header: 'parentId', value: (row) => row.parentId, verbatim: true },
|
|
19
|
+
{ header: 'author', value: (row) => row.author },
|
|
20
|
+
{ header: 'authorId', value: (row) => row.authorId, verbatim: true },
|
|
21
|
+
{ header: 'likeCount', value: (row) => row.likeCount },
|
|
22
|
+
{ header: 'isPinned', value: (row) => row.isPinned },
|
|
23
|
+
{ header: 'isUploader', value: (row) => row.isUploader },
|
|
24
|
+
{ header: 'isVerified', value: (row) => row.isVerified },
|
|
25
|
+
{ header: 'publishedAt', value: (row) => row.publishedAt },
|
|
26
|
+
{ header: 'text', value: (row) => row.text },
|
|
27
|
+
];
|
|
28
|
+
/** The tabs column is JSON written by SQLite; a damaged value reads as no extra tabs. */
|
|
29
|
+
function alsoInTabs(json) {
|
|
30
|
+
try {
|
|
31
|
+
return z.array(z.string()).catch([]).parse(JSON.parse(json));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function toVideoRow(row) {
|
|
38
|
+
return {
|
|
39
|
+
videoId: row.video_id,
|
|
40
|
+
...(row.channel_id === null ? {} : { channelId: row.channel_id }),
|
|
41
|
+
title: row.title,
|
|
42
|
+
...(row.duration_s === null ? {} : { durationSeconds: row.duration_s }),
|
|
43
|
+
...(row.upload_date === null ? {} : { uploadDate: row.upload_date }),
|
|
44
|
+
...(row.view_count === null ? {} : { viewCount: row.view_count }),
|
|
45
|
+
...(row.comment_count === null ? {} : { commentCount: row.comment_count }),
|
|
46
|
+
...(row.tab === null ? {} : { tab: row.tab }),
|
|
47
|
+
alsoInTabs: alsoInTabs(row.also_in_tabs),
|
|
48
|
+
detailed: row.detail_at !== null,
|
|
49
|
+
storedComments: row.stored_comments,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function toCommentRow(row) {
|
|
53
|
+
return {
|
|
54
|
+
commentId: row.comment_id,
|
|
55
|
+
videoId: row.video_id,
|
|
56
|
+
...(row.parent_id === null ? {} : { parentId: row.parent_id }),
|
|
57
|
+
author: row.author,
|
|
58
|
+
...(row.author_id === null ? {} : { authorId: row.author_id }),
|
|
59
|
+
likeCount: row.like_count,
|
|
60
|
+
isPinned: row.is_pinned,
|
|
61
|
+
isUploader: row.author_is_uploader,
|
|
62
|
+
isVerified: row.author_is_verified,
|
|
63
|
+
...(row.published_at === null
|
|
64
|
+
? {}
|
|
65
|
+
: { publishedAt: new Date(row.published_at * 1000).toISOString() }),
|
|
66
|
+
text: row.text,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=export-rows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export-rows.js","sourceRoot":"","sources":["../../src/utils/export-rows.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place this server runs ffmpeg.
|
|
3
|
+
*
|
|
4
|
+
* Every tool that looks at or cuts a video ends in an ffmpeg process, and each needs
|
|
5
|
+
* the same three things: it must stop when the client cancels the request, it must stop
|
|
6
|
+
* after a time budget, and its failure must reach the caller as a typed, actionable error
|
|
7
|
+
* rather than a raw process error. Kept here, a tool cannot forget one of them.
|
|
8
|
+
*/
|
|
9
|
+
/** JPEG quality on ffmpeg's 2 (best) to 31 (worst) scale. */
|
|
10
|
+
export declare const FFMPEG_JPEG_QUALITY = "4";
|
|
11
|
+
export interface FfmpegRun {
|
|
12
|
+
/** What was being done, as the failure message: "ffmpeg could not make that GIF." */
|
|
13
|
+
failure: string;
|
|
14
|
+
/** What the caller should try next. */
|
|
15
|
+
nextStep: string;
|
|
16
|
+
/** Milliseconds before the process is killed. 0 disables the budget. */
|
|
17
|
+
timeoutMs: number;
|
|
18
|
+
/** Bytes written to ffmpeg's standard input. */
|
|
19
|
+
input?: Uint8Array;
|
|
20
|
+
}
|
|
21
|
+
export interface FfmpegOutput {
|
|
22
|
+
stdout: Buffer;
|
|
23
|
+
stderr: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function runFfmpeg(args: string[], run: FfmpegRun): Promise<FfmpegOutput>;
|
|
26
|
+
//# sourceMappingURL=ffmpeg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ffmpeg.d.ts","sourceRoot":"","sources":["../../src/utils/ffmpeg.ts"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AAEH,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,MAAM,WAAW,SAAS;IACxB,qFAAqF;IACrF,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAYrF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { execa } from 'execa';
|
|
2
|
+
import { currentContext } from './context.js';
|
|
3
|
+
import { YouTubeError } from './errors.js';
|
|
4
|
+
/**
|
|
5
|
+
* The one place this server runs ffmpeg.
|
|
6
|
+
*
|
|
7
|
+
* Every tool that looks at or cuts a video ends in an ffmpeg process, and each needs
|
|
8
|
+
* the same three things: it must stop when the client cancels the request, it must stop
|
|
9
|
+
* after a time budget, and its failure must reach the caller as a typed, actionable error
|
|
10
|
+
* rather than a raw process error. Kept here, a tool cannot forget one of them.
|
|
11
|
+
*/
|
|
12
|
+
/** JPEG quality on ffmpeg's 2 (best) to 31 (worst) scale. */
|
|
13
|
+
export const FFMPEG_JPEG_QUALITY = '4';
|
|
14
|
+
export async function runFfmpeg(args, run) {
|
|
15
|
+
try {
|
|
16
|
+
const result = await execa('ffmpeg', args, {
|
|
17
|
+
encoding: 'buffer',
|
|
18
|
+
timeout: run.timeoutMs,
|
|
19
|
+
cancelSignal: currentContext().signal,
|
|
20
|
+
...(run.input === undefined ? {} : { input: run.input }),
|
|
21
|
+
});
|
|
22
|
+
return { stdout: Buffer.from(result.stdout), stderr: Buffer.from(result.stderr).toString() };
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
throw new YouTubeError('YTDLP_FAILED', run.failure, { nextStep: run.nextStep, cause: error });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=ffmpeg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ffmpeg.js","sourceRoot":"","sources":["../../src/utils/ffmpeg.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;;GAOG;AAEH,6DAA6D;AAC7D,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAkBvC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc,EAAE,GAAc;IAC5D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE;YACzC,QAAQ,EAAE,QAAQ;YAClB,OAAO,EAAE,GAAG,CAAC,SAAS;YACtB,YAAY,EAAE,cAAc,EAAE,CAAC,MAAM;YACrC,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC;SACzD,CAAC,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC/F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,GAAG,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAChG,CAAC;AACH,CAAC"}
|
package/dist/utils/format.d.ts
CHANGED
|
@@ -20,7 +20,23 @@ export interface PageInfo {
|
|
|
20
20
|
hasMore: boolean;
|
|
21
21
|
nextOffset?: number;
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Takes an object, not three adjacent numbers.
|
|
25
|
+
*
|
|
26
|
+
* The bug this replaces was not in the arithmetic: it was five call sites that
|
|
27
|
+
* passed `count` where `total` belonged, so `hasMore` computed `n < n` and a
|
|
28
|
+
* caller with 20 of a channel's 5,000 videos was told it had all of them.
|
|
29
|
+
* Positional arguments of the same type made that invisible; an object makes
|
|
30
|
+
* it impossible, and changing the signature forced every call site to be
|
|
31
|
+
* revisited, which was the point.
|
|
32
|
+
*/
|
|
33
|
+
export interface PageInput {
|
|
34
|
+
/** Items available in total. Never `count` — that is the bug this shape prevents. */
|
|
35
|
+
total: number;
|
|
36
|
+
count: number;
|
|
37
|
+
offset?: number;
|
|
38
|
+
}
|
|
39
|
+
export declare function pageInfo({ total, count, offset }: PageInput): PageInfo;
|
|
24
40
|
/** A link to a file this server produced, so clients can offer to open it. */
|
|
25
41
|
export declare function fileResourceLink(filePath: string, name: string, mimeType?: string): {
|
|
26
42
|
type: 'resource_link';
|
|
@@ -28,6 +44,12 @@ export declare function fileResourceLink(filePath: string, name: string, mimeTyp
|
|
|
28
44
|
name: string;
|
|
29
45
|
mimeType?: string;
|
|
30
46
|
};
|
|
47
|
+
/** A picture the model can look at, as an MCP image block. */
|
|
48
|
+
export declare function imageBlock(bytes: Uint8Array, mimeType: string): {
|
|
49
|
+
type: 'image';
|
|
50
|
+
data: string;
|
|
51
|
+
mimeType: string;
|
|
52
|
+
};
|
|
31
53
|
/** A result that both reads well and points at the file it produced. */
|
|
32
54
|
export declare function fileResult(text: string, structured: Record<string, unknown>, file: {
|
|
33
55
|
path: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AAOD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CAK5F;AAED,wEAAwE;AACxE,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AAOD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG1D;AAED,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAKrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,cAAc,CAK5F;AAED,wEAAwE;AACxE,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,SAAS;IACxB,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAU,EAAE,EAAE,SAAS,GAAG,QAAQ,CAS1E;AAED,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAOzE;AAED,8DAA8D;AAC9D,wBAAgB,UAAU,CACxB,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,GACf;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAEnD;AAED,wEAAwE;AACxE,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD,cAAc,CAQhB"}
|
package/dist/utils/format.js
CHANGED
|
@@ -40,7 +40,7 @@ export function toolResult(text, structured) {
|
|
|
40
40
|
structuredContent: structured,
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
|
-
export function pageInfo(total, count, offset = 0) {
|
|
43
|
+
export function pageInfo({ total, count, offset = 0 }) {
|
|
44
44
|
const hasMore = offset + count < total;
|
|
45
45
|
return {
|
|
46
46
|
total,
|
|
@@ -59,6 +59,10 @@ export function fileResourceLink(filePath, name, mimeType) {
|
|
|
59
59
|
...(mimeType === undefined ? {} : { mimeType }),
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
+
/** A picture the model can look at, as an MCP image block. */
|
|
63
|
+
export function imageBlock(bytes, mimeType) {
|
|
64
|
+
return { type: 'image', data: Buffer.from(bytes).toString('base64'), mimeType };
|
|
65
|
+
}
|
|
62
66
|
/** A result that both reads well and points at the file it produced. */
|
|
63
67
|
export function fileResult(text, structured, file) {
|
|
64
68
|
return {
|
package/dist/utils/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,IAAI,KAAK,IAAI,aAAa;QAAE,OAAO,GAAG,gBAAgB,CAAC,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC;IACjF,IAAI,KAAK,IAAI,SAAS;QAAE,OAAO,GAAG,gBAAgB,CAAC,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC;IACzE,IAAI,KAAK,IAAI,KAAK;QAAE,OAAO,GAAG,gBAAgB,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC;IACjE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,gBAAgB,CAAC,CAAS;IACjC,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChD,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAClE,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAChF,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AACzD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,UAAmC;IAC1E,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC;QAC1C,iBAAiB,EAAE,UAAU;KAC9B,CAAC;AACJ,CAAC;
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deciding which moments of a video to look at.
|
|
3
|
+
*
|
|
4
|
+
* Pure, so every rule about what is a valid request lives in one place and is
|
|
5
|
+
* testable without a video: the tools that take pictures only ever receive a
|
|
6
|
+
* sorted list of timestamps that are known to exist.
|
|
7
|
+
*/
|
|
8
|
+
export interface FramePlanRequest {
|
|
9
|
+
/** Exact moments, in seconds. Takes precedence over `count`. */
|
|
10
|
+
timestamps?: number[];
|
|
11
|
+
/** How many frames to spread evenly across the window. */
|
|
12
|
+
count?: number;
|
|
13
|
+
/** Window start in seconds. Default: 0. */
|
|
14
|
+
start?: number;
|
|
15
|
+
/** Window end in seconds. Default: the end of the video. */
|
|
16
|
+
end?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The sorted timestamps to capture.
|
|
20
|
+
*
|
|
21
|
+
* `duration` of 0 means unknown, in which case nothing can be bounded by it.
|
|
22
|
+
*/
|
|
23
|
+
export declare function planTimestamps(request: FramePlanRequest, duration: number, max: number): number[];
|
|
24
|
+
//# sourceMappingURL=frame-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-plan.d.ts","sourceRoot":"","sources":["../../src/utils/frame-plan.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAmDD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAcjG"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { YouTubeError } from './errors.js';
|
|
2
|
+
import { roundToMillisecond } from './seconds.js';
|
|
3
|
+
function pastTheEnd(seconds, duration) {
|
|
4
|
+
return new YouTubeError('INVALID_INPUT', `${Math.round(seconds)}s is past the end of this ${duration}s video.`, { nextStep: 'Pick a moment inside the video.' });
|
|
5
|
+
}
|
|
6
|
+
function tooMany(max) {
|
|
7
|
+
return new YouTubeError('INVALID_INPUT', `Ask for at most ${max} frames per call.`, {
|
|
8
|
+
nextStep: 'Narrow the window, or make several calls.',
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function explicitTimestamps(timestamps, duration, max) {
|
|
12
|
+
const unique = [...new Set(timestamps)].sort((a, b) => a - b);
|
|
13
|
+
if (unique.length > max)
|
|
14
|
+
throw tooMany(max);
|
|
15
|
+
const beyond = unique.find((seconds) => duration > 0 && seconds >= duration);
|
|
16
|
+
if (beyond !== undefined)
|
|
17
|
+
throw pastTheEnd(beyond, duration);
|
|
18
|
+
return unique;
|
|
19
|
+
}
|
|
20
|
+
function spacedTimestamps(request, duration, max) {
|
|
21
|
+
const { count, start = 0 } = request;
|
|
22
|
+
if (count === undefined || count < 1) {
|
|
23
|
+
throw new YouTubeError('INVALID_INPUT', 'Pass at least 1 for count.');
|
|
24
|
+
}
|
|
25
|
+
if (count > max)
|
|
26
|
+
throw tooMany(max);
|
|
27
|
+
if (duration > 0 && start >= duration)
|
|
28
|
+
throw pastTheEnd(start, duration);
|
|
29
|
+
const requestedEnd = request.end ?? duration;
|
|
30
|
+
if (requestedEnd <= 0) {
|
|
31
|
+
throw new YouTubeError('INVALID_INPUT', 'Pass end when the video duration is unknown.');
|
|
32
|
+
}
|
|
33
|
+
if (requestedEnd <= start) {
|
|
34
|
+
throw new YouTubeError('INVALID_INPUT', 'end must be later than start.');
|
|
35
|
+
}
|
|
36
|
+
const end = duration > 0 ? Math.min(requestedEnd, duration) : requestedEnd;
|
|
37
|
+
const slice = (end - start) / count;
|
|
38
|
+
// Midpoints: the first frame of a video is often black and the last may not exist.
|
|
39
|
+
return Array.from({ length: count }, (_, index) => roundToMillisecond(start + (index + 0.5) * slice));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The sorted timestamps to capture.
|
|
43
|
+
*
|
|
44
|
+
* `duration` of 0 means unknown, in which case nothing can be bounded by it.
|
|
45
|
+
*/
|
|
46
|
+
export function planTimestamps(request, duration, max) {
|
|
47
|
+
const { timestamps, count } = request;
|
|
48
|
+
if (timestamps !== undefined && timestamps.length > 0) {
|
|
49
|
+
return explicitTimestamps(timestamps, duration, max);
|
|
50
|
+
}
|
|
51
|
+
if (count === undefined) {
|
|
52
|
+
throw new YouTubeError('INVALID_INPUT', 'Provide timestamps or a count.', {
|
|
53
|
+
nextStep: 'Pass timestamps for exact moments, or count to spread frames across the video.',
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return spacedTimestamps(request, duration, max);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=frame-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-plan.js","sourceRoot":"","sources":["../../src/utils/frame-plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type FramePlanRequest } from './frame-plan.js';
|
|
2
|
+
/**
|
|
3
|
+
* Looking at a video: stills captured into memory, never onto disk.
|
|
4
|
+
*
|
|
5
|
+
* A picture returned inline is something the model can read, which is what makes
|
|
6
|
+
* "what is on screen at 3:20" answerable without a human in the loop. Nothing is
|
|
7
|
+
* written, so the same code serves a contact sheet and a handful of stills.
|
|
8
|
+
*/
|
|
9
|
+
/** Each frame is an inline image, so a call is bounded by what a reply can carry. */
|
|
10
|
+
export declare const MAX_FRAMES_PER_CALL = 12;
|
|
11
|
+
/** Wide enough to read slide text, small enough to be about 100 KB a frame. */
|
|
12
|
+
export declare const DEFAULT_FRAME_WIDTH = 768;
|
|
13
|
+
export interface CapturedFrame {
|
|
14
|
+
timestamp: number;
|
|
15
|
+
bytes: Buffer;
|
|
16
|
+
}
|
|
17
|
+
export interface FrameBatch {
|
|
18
|
+
videoId: string;
|
|
19
|
+
title: string;
|
|
20
|
+
duration: number;
|
|
21
|
+
frames: CapturedFrame[];
|
|
22
|
+
}
|
|
23
|
+
export interface CaptureOptions {
|
|
24
|
+
/** Frames are shrunk to this width, never enlarged. */
|
|
25
|
+
width: number;
|
|
26
|
+
maxFrames: number;
|
|
27
|
+
/** The tool asking, for the ffmpeg preflight message and yt-dlp logs. */
|
|
28
|
+
label: string;
|
|
29
|
+
}
|
|
30
|
+
/** Capture the frames a request describes, in timestamp order. */
|
|
31
|
+
export declare function captureFrames(video: string, request: FramePlanRequest, options: CaptureOptions): Promise<FrameBatch>;
|
|
32
|
+
//# sourceMappingURL=frames.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frames.d.ts","sourceRoot":"","sources":["../../src/utils/frames.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAUxE;;;;;;GAMG;AAEH,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAKvC,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;CACf;AAqCD,kEAAkE;AAClE,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC,CAerB"}
|