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,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the records that belong to a channel, from whatever the caller called it.
|
|
3
|
+
*
|
|
4
|
+
* Every local tool answers from disk, so none of them should need the network
|
|
5
|
+
* to work out which channel is meant — asking YouTube to resolve a handle
|
|
6
|
+
* before every search would put a round trip in front of a lookup that is
|
|
7
|
+
* otherwise instant, and would make the data unusable offline. A stored record
|
|
8
|
+
* already carries the channel's id, handle, name and URL, which is everything
|
|
9
|
+
* a caller is likely to type.
|
|
10
|
+
*/
|
|
11
|
+
const CHANNEL_ID_IN_TEXT = /UC[A-Za-z0-9_-]{22}/;
|
|
12
|
+
const HANDLE_IN_TEXT = /@[\w.-]+/;
|
|
13
|
+
export function findByChannel(items, channel) {
|
|
14
|
+
const wanted = candidates(channel);
|
|
15
|
+
return items.filter((item) => keysOf(item.channel).some((key) => wanted.has(key)));
|
|
16
|
+
}
|
|
17
|
+
function candidates(channel) {
|
|
18
|
+
const keys = new Set([normalize(channel)]);
|
|
19
|
+
const id = CHANNEL_ID_IN_TEXT.exec(channel)?.[0];
|
|
20
|
+
if (id !== undefined)
|
|
21
|
+
keys.add(normalize(id));
|
|
22
|
+
const handle = HANDLE_IN_TEXT.exec(channel)?.[0];
|
|
23
|
+
if (handle !== undefined) {
|
|
24
|
+
keys.add(normalize(handle));
|
|
25
|
+
keys.add(normalize(handle.slice(1)));
|
|
26
|
+
}
|
|
27
|
+
return keys;
|
|
28
|
+
}
|
|
29
|
+
function keysOf({ channelId, handle, name, channelUrl }) {
|
|
30
|
+
return [channelId, handle, handle.replace(/^@/, ''), name, channelUrl]
|
|
31
|
+
.map(normalize)
|
|
32
|
+
.filter((key) => key !== '');
|
|
33
|
+
}
|
|
34
|
+
function normalize(value) {
|
|
35
|
+
return value.trim().toLowerCase().replace(/\/+$/, '');
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=channel-lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-lookup.js","sourceRoot":"","sources":["../../src/utils/channel-lookup.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AACjD,MAAM,cAAc,GAAG,UAAU,CAAC;AAElC,MAAM,UAAU,aAAa,CAC3B,KAAU,EACV,OAAe;IAEf,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,SAAS,UAAU,CAAC,OAAe;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAEnD,MAAM,EAAE,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjD,IAAI,EAAE,KAAK,SAAS;QAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,MAAM,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAe;IAClE,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC;SACnE,GAAG,CAAC,SAAS,CAAC;SACd,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Chapter } from './youtube.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolving a chapter name a caller typed to a chapter the video actually has.
|
|
4
|
+
*
|
|
5
|
+
* Exact title first, then a substring, both case-insensitive: people remember
|
|
6
|
+
* "the middle" of a talk, not "03 — The Middle Section (part 2)". Two tools
|
|
7
|
+
* used to carry identical copies of this; a mismatch in how they matched
|
|
8
|
+
* would have meant a chapter that clips but cannot be read, or the reverse.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveChapter(chapters: Chapter[], name: string, whenNone: string): Chapter;
|
|
11
|
+
//# sourceMappingURL=chapters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chapters.d.ts","sourceRoot":"","sources":["../../src/utils/chapters.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAiB3F"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { YouTubeError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolving a chapter name a caller typed to a chapter the video actually has.
|
|
4
|
+
*
|
|
5
|
+
* Exact title first, then a substring, both case-insensitive: people remember
|
|
6
|
+
* "the middle" of a talk, not "03 — The Middle Section (part 2)". Two tools
|
|
7
|
+
* used to carry identical copies of this; a mismatch in how they matched
|
|
8
|
+
* would have meant a chapter that clips but cannot be read, or the reverse.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveChapter(chapters, name, whenNone) {
|
|
11
|
+
if (chapters.length === 0) {
|
|
12
|
+
throw new YouTubeError('NOT_FOUND', 'This video has no chapters.', { nextStep: whenNone });
|
|
13
|
+
}
|
|
14
|
+
const wanted = name.toLowerCase();
|
|
15
|
+
const match = chapters.find((chapter) => chapter.title.toLowerCase() === wanted) ??
|
|
16
|
+
chapters.find((chapter) => chapter.title.toLowerCase().includes(wanted));
|
|
17
|
+
if (!match) {
|
|
18
|
+
throw new YouTubeError('NOT_FOUND', `No chapter matches "${name}".`, {
|
|
19
|
+
nextStep: `Available chapters: ${chapters.map((chapter) => chapter.title).join(', ')}`,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return match;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=chapters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chapters.js","sourceRoot":"","sources":["../../src/utils/chapters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,QAAmB,EAAE,IAAY,EAAE,QAAgB;IAChF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,6BAA6B,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAClC,MAAM,KAAK,GACT,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;QAClE,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAE3E,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,uBAAuB,IAAI,IAAI,EAAE;YACnE,QAAQ,EAAE,uBAAuB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SACvF,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clips.d.ts","sourceRoot":"","sources":["../../src/utils/clips.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"clips.d.ts","sourceRoot":"","sources":["../../src/utils/clips.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgBhE;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D,OAAO,CAAC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAwCjF;AAMD,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAChE,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,UAAU,CAAC,CA2DrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,KAAK,GAAG,KAAK,CAAA;CAAE,GACrD,OAAO,CAAC,WAAW,CAAC,CAoEtB;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
|
package/dist/utils/clips.js
CHANGED
|
@@ -5,8 +5,10 @@ import { YouTubeError } from './errors.js';
|
|
|
5
5
|
import { requireFfmpeg } from './preflight.js';
|
|
6
6
|
import { resolveOutputDir } from './validate.js';
|
|
7
7
|
import { TIMEOUTS, runYtDlp } from './ytdlp.js';
|
|
8
|
+
import { AFTER_MOVE_TEMPLATE, readAfterMove } from './youtube-download.js';
|
|
8
9
|
import { formatSrtTimestamp } from './transcript.js';
|
|
9
10
|
import { getChapters, getVideoInfo } from './youtube.js';
|
|
11
|
+
import { resolveChapter } from './chapters.js';
|
|
10
12
|
import { dataDir } from './paths.js';
|
|
11
13
|
/**
|
|
12
14
|
* Pulling usable excerpts out of a video for editing.
|
|
@@ -53,20 +55,7 @@ export async function resolveRange(video, options) {
|
|
|
53
55
|
const info = await getVideoInfo(video);
|
|
54
56
|
let range;
|
|
55
57
|
if (options.chapter !== undefined) {
|
|
56
|
-
const
|
|
57
|
-
if (chapters.length === 0) {
|
|
58
|
-
throw new YouTubeError('NOT_FOUND', 'This video has no chapters.', {
|
|
59
|
-
nextStep: 'Pass start and end instead.',
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
const wanted = options.chapter.toLowerCase();
|
|
63
|
-
const match = chapters.find((chapter) => chapter.title.toLowerCase() === wanted) ??
|
|
64
|
-
chapters.find((chapter) => chapter.title.toLowerCase().includes(wanted));
|
|
65
|
-
if (!match) {
|
|
66
|
-
throw new YouTubeError('NOT_FOUND', `No chapter matches "${options.chapter}".`, {
|
|
67
|
-
nextStep: `Available chapters: ${chapters.map((chapter) => chapter.title).join(', ')}`,
|
|
68
|
-
});
|
|
69
|
-
}
|
|
58
|
+
const match = resolveChapter(await getChapters(video), options.chapter, 'Pass start and end instead.');
|
|
70
59
|
range = { start: match.startTime, end: match.endTime };
|
|
71
60
|
}
|
|
72
61
|
else {
|
|
@@ -123,18 +112,18 @@ export async function extractClip(video, rangeOptions, options) {
|
|
|
123
112
|
else {
|
|
124
113
|
args.push('--merge-output-format', options.container);
|
|
125
114
|
}
|
|
126
|
-
|
|
127
|
-
|
|
115
|
+
// The after_move stage fires once post-processing has produced the final
|
|
116
|
+
// file, so the path it prints is the real one — extension included — and
|
|
117
|
+
// nothing has to be spawned a second time to learn it.
|
|
118
|
+
args.push('--print', AFTER_MOVE_TEMPLATE);
|
|
119
|
+
const stdout = await runYtDlp(args, {
|
|
128
120
|
label: 'extract_clip',
|
|
129
121
|
timeoutMs: TIMEOUTS.download,
|
|
130
122
|
retry: false,
|
|
123
|
+
target: url,
|
|
131
124
|
});
|
|
132
|
-
const filenameOutput = await runYtDlp([...args.slice(0, -1), '--print', 'filename', '--skip-download', url], { label: 'extract_clip (filename)' });
|
|
133
125
|
const extension = options.audioOnly ? (options.audioFormat ?? 'mp3') : options.container;
|
|
134
|
-
const
|
|
135
|
-
const filePath = reported === ''
|
|
136
|
-
? join(targetDir, `${stem}.${extension}`)
|
|
137
|
-
: replaceExtension(reported, extension);
|
|
126
|
+
const filePath = stdout.trim() === '' ? join(targetDir, `${stem}.${extension}`) : readAfterMove(stdout).filepath;
|
|
138
127
|
return {
|
|
139
128
|
videoId,
|
|
140
129
|
title,
|
|
@@ -144,9 +133,6 @@ export async function extractClip(video, rangeOptions, options) {
|
|
|
144
133
|
duration: range.end - range.start,
|
|
145
134
|
};
|
|
146
135
|
}
|
|
147
|
-
function replaceExtension(path, extension) {
|
|
148
|
-
return path.replace(/\.[^./\\]+$/, `.${extension}`);
|
|
149
|
-
}
|
|
150
136
|
/**
|
|
151
137
|
* Grab a single still.
|
|
152
138
|
*
|
|
@@ -162,12 +148,16 @@ export async function extractFrame(video, timestamp, options) {
|
|
|
162
148
|
}
|
|
163
149
|
await mkdir(targetDir, { recursive: true });
|
|
164
150
|
const url = `https://www.youtube.com/watch?v=${info.id}`;
|
|
165
|
-
const mediaUrl = (await runYtDlp(['-f', 'bestvideo[height<=1080]/best', '--get-url', '--no-playlist'
|
|
151
|
+
const mediaUrl = (await runYtDlp(['-f', 'bestvideo[height<=1080]/best', '--get-url', '--no-playlist'], {
|
|
166
152
|
label: 'extract_frame (resolve)',
|
|
153
|
+
target: url,
|
|
167
154
|
}))
|
|
168
155
|
.trim()
|
|
169
156
|
.split('\n')[0];
|
|
170
|
-
|
|
157
|
+
// ffmpeg accepts file:, pipe:, concat: and more as inputs; the only thing
|
|
158
|
+
// yt-dlp should have resolved is an https media URL, and the allowlist
|
|
159
|
+
// below makes ffmpeg refuse anything else even if it had.
|
|
160
|
+
if (!mediaUrl?.startsWith('https://')) {
|
|
171
161
|
throw new YouTubeError('YTDLP_FAILED', 'Could not resolve a video stream for this video.', {
|
|
172
162
|
nextStep: 'Check the video is playable, or call check_health.',
|
|
173
163
|
});
|
|
@@ -175,6 +165,8 @@ export async function extractFrame(video, timestamp, options) {
|
|
|
175
165
|
const filePath = join(targetDir, `${safeStem(info.title, info.id)} [${Math.round(timestamp)}s].${options.format}`);
|
|
176
166
|
try {
|
|
177
167
|
await execa('ffmpeg', [
|
|
168
|
+
'-protocol_whitelist',
|
|
169
|
+
'https,tls,tcp,crypto',
|
|
178
170
|
// -ss before -i seeks without decoding everything up to that point.
|
|
179
171
|
'-ss',
|
|
180
172
|
String(timestamp),
|
package/dist/utils/clips.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clips.js","sourceRoot":"","sources":["../../src/utils/clips.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC;;;;;;;GAOG;AAEH,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AACnC,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AACrC,MAAM,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;AAgB3C;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,QAAgB;IACtD,MAAM,OAAO,GAAG,KAAK;QACnB,yEAAyE;QACzE,qEAAqE;SACpE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;QACrB,sEAAsE;QACtE,uEAAuE;SACtE,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;SACtB,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;QAC9B,kFAAkF;SACjF,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;SACnB,IAAI,EAAE;SACN,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,IAAI,EAAE,CAAC;IAEV,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;AAC7C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAa,EACb,OAA2D;IAE3D,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC;IAEvC,IAAI,KAAgB,CAAC;IAErB,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,
|
|
1
|
+
{"version":3,"file":"clips.js","sourceRoot":"","sources":["../../src/utils/clips.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { CommentThread } from './comment-threads.js';
|
|
4
|
+
/**
|
|
5
|
+
* Comments on disk.
|
|
6
|
+
*
|
|
7
|
+
* Upserted on the comment id, never INSERT OR REPLACE: replace deletes and
|
|
8
|
+
* reinserts, which changes the rowid the external-content FTS index is keyed
|
|
9
|
+
* on. Upsert keeps it and fires the update trigger, so a re-harvest costs
|
|
10
|
+
* network and never data.
|
|
11
|
+
*/
|
|
12
|
+
export declare const COMMENT_COMMIT_BATCH = 2000;
|
|
13
|
+
declare const commentRowSchema: z.ZodObject<{
|
|
14
|
+
comment_id: z.ZodString;
|
|
15
|
+
video_id: z.ZodString;
|
|
16
|
+
parent_id: z.ZodNullable<z.ZodString>;
|
|
17
|
+
text: z.ZodString;
|
|
18
|
+
like_count: z.ZodNumber;
|
|
19
|
+
author: z.ZodString;
|
|
20
|
+
author_id: z.ZodNullable<z.ZodString>;
|
|
21
|
+
author_is_uploader: z.ZodEffects<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<1>]>, boolean, 0 | 1>;
|
|
22
|
+
author_is_verified: z.ZodEffects<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<1>]>, boolean, 0 | 1>;
|
|
23
|
+
is_pinned: z.ZodEffects<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<1>]>, boolean, 0 | 1>;
|
|
24
|
+
is_favorited: z.ZodEffects<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<1>]>, boolean, 0 | 1>;
|
|
25
|
+
published_at: z.ZodNullable<z.ZodNumber>;
|
|
26
|
+
}, "strip", z.ZodTypeAny, {
|
|
27
|
+
author: string;
|
|
28
|
+
text: string;
|
|
29
|
+
like_count: number;
|
|
30
|
+
author_id: string | null;
|
|
31
|
+
author_is_uploader: boolean;
|
|
32
|
+
author_is_verified: boolean;
|
|
33
|
+
is_pinned: boolean;
|
|
34
|
+
is_favorited: boolean;
|
|
35
|
+
comment_id: string;
|
|
36
|
+
video_id: string;
|
|
37
|
+
parent_id: string | null;
|
|
38
|
+
published_at: number | null;
|
|
39
|
+
}, {
|
|
40
|
+
author: string;
|
|
41
|
+
text: string;
|
|
42
|
+
like_count: number;
|
|
43
|
+
author_id: string | null;
|
|
44
|
+
author_is_uploader: 0 | 1;
|
|
45
|
+
author_is_verified: 0 | 1;
|
|
46
|
+
is_pinned: 0 | 1;
|
|
47
|
+
is_favorited: 0 | 1;
|
|
48
|
+
comment_id: string;
|
|
49
|
+
video_id: string;
|
|
50
|
+
parent_id: string | null;
|
|
51
|
+
published_at: number | null;
|
|
52
|
+
}>;
|
|
53
|
+
export type StoredComment = z.infer<typeof commentRowSchema>;
|
|
54
|
+
/** Writes threads and their replies. The caller owns the transaction. */
|
|
55
|
+
export declare function saveThreads(database: DatabaseSync, videoId: string, threads: CommentThread[], now?: number): number;
|
|
56
|
+
export interface CommentQuery {
|
|
57
|
+
videoId?: string;
|
|
58
|
+
channelId?: string;
|
|
59
|
+
match?: string;
|
|
60
|
+
authorId?: string;
|
|
61
|
+
minLikes?: number;
|
|
62
|
+
topLevelOnly?: boolean;
|
|
63
|
+
threadOf?: string;
|
|
64
|
+
order?: 'likes' | 'newest' | 'oldest' | 'relevance';
|
|
65
|
+
limit?: number;
|
|
66
|
+
offset?: number;
|
|
67
|
+
}
|
|
68
|
+
export declare function queryComments(database: DatabaseSync, query: CommentQuery): StoredComment[];
|
|
69
|
+
export declare function countComments(database: DatabaseSync, query: CommentQuery): number;
|
|
70
|
+
export {};
|
|
71
|
+
//# sourceMappingURL=comment-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment-store.d.ts","sourceRoot":"","sources":["../../src/utils/comment-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAmB,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG3E;;;;;;;GAOG;AAEH,eAAO,MAAM,oBAAoB,OAAQ,CAAC;AAe1C,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAapB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAuB7D,yEAAyE;AACzE,wBAAgB,WAAW,CACzB,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,aAAa,EAAE,EACxB,GAAG,SAAa,GACf,MAAM,CAcR;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA8CD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,aAAa,EAAE,CAoB1F;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAQjF"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { queryRows, sqliteBoolean } from './store-rows.js';
|
|
3
|
+
/**
|
|
4
|
+
* Comments on disk.
|
|
5
|
+
*
|
|
6
|
+
* Upserted on the comment id, never INSERT OR REPLACE: replace deletes and
|
|
7
|
+
* reinserts, which changes the rowid the external-content FTS index is keyed
|
|
8
|
+
* on. Upsert keeps it and fires the update trigger, so a re-harvest costs
|
|
9
|
+
* network and never data.
|
|
10
|
+
*/
|
|
11
|
+
export const COMMENT_COMMIT_BATCH = 2_000;
|
|
12
|
+
const UPSERT = `
|
|
13
|
+
INSERT INTO comment (
|
|
14
|
+
comment_id, video_id, parent_id, text, like_count, author, author_id, author_url,
|
|
15
|
+
author_thumbnail, author_is_uploader, author_is_verified, is_pinned, is_favorited,
|
|
16
|
+
time_text, published_at, harvested_at
|
|
17
|
+
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
18
|
+
ON CONFLICT(comment_id) DO UPDATE SET
|
|
19
|
+
text = excluded.text,
|
|
20
|
+
like_count = excluded.like_count,
|
|
21
|
+
is_pinned = excluded.is_pinned,
|
|
22
|
+
is_favorited = excluded.is_favorited,
|
|
23
|
+
harvested_at = excluded.harvested_at`;
|
|
24
|
+
const commentRowSchema = z.object({
|
|
25
|
+
comment_id: z.string(),
|
|
26
|
+
video_id: z.string(),
|
|
27
|
+
parent_id: z.string().nullable(),
|
|
28
|
+
text: z.string(),
|
|
29
|
+
like_count: z.number(),
|
|
30
|
+
author: z.string(),
|
|
31
|
+
author_id: z.string().nullable(),
|
|
32
|
+
author_is_uploader: sqliteBoolean,
|
|
33
|
+
author_is_verified: sqliteBoolean,
|
|
34
|
+
is_pinned: sqliteBoolean,
|
|
35
|
+
is_favorited: sqliteBoolean,
|
|
36
|
+
published_at: z.number().nullable(),
|
|
37
|
+
});
|
|
38
|
+
function bind(comment, videoId, now) {
|
|
39
|
+
return [
|
|
40
|
+
comment.id,
|
|
41
|
+
videoId,
|
|
42
|
+
comment.parentId,
|
|
43
|
+
comment.text,
|
|
44
|
+
comment.likeCount,
|
|
45
|
+
comment.author,
|
|
46
|
+
comment.authorId ?? null,
|
|
47
|
+
comment.authorUrl ?? null,
|
|
48
|
+
comment.authorThumbnailUrl ?? null,
|
|
49
|
+
Number(comment.authorIsUploader),
|
|
50
|
+
Number(comment.authorIsVerified),
|
|
51
|
+
Number(comment.isPinned),
|
|
52
|
+
Number(comment.isFavorited),
|
|
53
|
+
comment.timeText ?? null,
|
|
54
|
+
comment.timestamp ?? null,
|
|
55
|
+
now,
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
/** Writes threads and their replies. The caller owns the transaction. */
|
|
59
|
+
export function saveThreads(database, videoId, threads, now = Date.now()) {
|
|
60
|
+
const statement = database.prepare(UPSERT);
|
|
61
|
+
let written = 0;
|
|
62
|
+
for (const thread of threads) {
|
|
63
|
+
statement.run(...bind(thread.comment, videoId, now));
|
|
64
|
+
written += 1;
|
|
65
|
+
for (const reply of thread.replies) {
|
|
66
|
+
statement.run(...bind(reply, videoId, now));
|
|
67
|
+
written += 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return written;
|
|
71
|
+
}
|
|
72
|
+
function buildWhere(query) {
|
|
73
|
+
const clauses = [];
|
|
74
|
+
const params = [];
|
|
75
|
+
if (query.videoId !== undefined) {
|
|
76
|
+
clauses.push('c.video_id = ?');
|
|
77
|
+
params.push(query.videoId);
|
|
78
|
+
}
|
|
79
|
+
if (query.channelId !== undefined) {
|
|
80
|
+
clauses.push('c.video_id IN (SELECT video_id FROM video WHERE channel_id = ?)');
|
|
81
|
+
params.push(query.channelId);
|
|
82
|
+
}
|
|
83
|
+
if (query.authorId !== undefined) {
|
|
84
|
+
clauses.push('(c.author_id = ? OR c.author = ?)');
|
|
85
|
+
params.push(query.authorId, query.authorId);
|
|
86
|
+
}
|
|
87
|
+
if (query.threadOf !== undefined) {
|
|
88
|
+
clauses.push('(c.comment_id = ? OR c.parent_id = ?)');
|
|
89
|
+
params.push(query.threadOf, query.threadOf);
|
|
90
|
+
}
|
|
91
|
+
if (query.topLevelOnly === true)
|
|
92
|
+
clauses.push('c.parent_id IS NULL');
|
|
93
|
+
if (query.minLikes !== undefined && query.minLikes > 0) {
|
|
94
|
+
clauses.push('c.like_count >= ?');
|
|
95
|
+
params.push(query.minLikes);
|
|
96
|
+
}
|
|
97
|
+
if (query.match !== undefined && query.match !== '') {
|
|
98
|
+
// Bound, never concatenated: `match` is caller text and FTS5 has its own
|
|
99
|
+
// expression syntax that must not reach the SQL parser.
|
|
100
|
+
clauses.push('c.rowid IN (SELECT rowid FROM comment_fts WHERE comment_fts MATCH ?)');
|
|
101
|
+
params.push(query.match);
|
|
102
|
+
}
|
|
103
|
+
return { sql: clauses.length === 0 ? '' : ` WHERE ${clauses.join(' AND ')}`, params };
|
|
104
|
+
}
|
|
105
|
+
const ORDER = {
|
|
106
|
+
likes: 'c.like_count DESC, c.comment_id',
|
|
107
|
+
newest: 'c.published_at DESC, c.comment_id',
|
|
108
|
+
oldest: 'c.published_at ASC, c.comment_id',
|
|
109
|
+
// Threads read in reply order; relevance needs the FTS rank, which only
|
|
110
|
+
// exists when a match was given, so it falls back to likes.
|
|
111
|
+
relevance: 'c.like_count DESC, c.comment_id',
|
|
112
|
+
};
|
|
113
|
+
export function queryComments(database, query) {
|
|
114
|
+
const where = buildWhere(query);
|
|
115
|
+
const order = query.threadOf === undefined
|
|
116
|
+
? ORDER[query.order ?? 'likes']
|
|
117
|
+
: 'c.parent_id NULLS FIRST, c.published_at';
|
|
118
|
+
return queryRows(database.prepare(`SELECT c.comment_id, c.video_id, c.parent_id, c.text, c.like_count, c.author,
|
|
119
|
+
c.author_id, c.author_is_uploader, c.author_is_verified, c.is_pinned,
|
|
120
|
+
c.is_favorited, c.published_at
|
|
121
|
+
FROM comment c${where.sql}
|
|
122
|
+
ORDER BY ${order} LIMIT ? OFFSET ?`), commentRowSchema, ...where.params, query.limit ?? 25, query.offset ?? 0);
|
|
123
|
+
}
|
|
124
|
+
export function countComments(database, query) {
|
|
125
|
+
const where = buildWhere(query);
|
|
126
|
+
const rows = queryRows(database.prepare(`SELECT COUNT(*) AS n FROM comment c${where.sql}`), z.object({ n: z.number() }), ...where.params);
|
|
127
|
+
return rows[0]?.n ?? 0;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=comment-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment-store.js","sourceRoot":"","sources":["../../src/utils/comment-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { commentRowSchema } from './youtube-schemas.js';
|
|
3
|
+
/**
|
|
4
|
+
* Rebuilding threads from yt-dlp's flat comment array.
|
|
5
|
+
*
|
|
6
|
+
* yt-dlp returns replies alongside their parents and marks each with a
|
|
7
|
+
* `parent` of `root` or the parent's id. The previous implementation filtered
|
|
8
|
+
* the replies away *after* paying to fetch them, which is the most expensive
|
|
9
|
+
* possible way to have no replies.
|
|
10
|
+
*/
|
|
11
|
+
export type CommentRow = z.infer<typeof commentRowSchema>;
|
|
12
|
+
export interface ThreadedComment {
|
|
13
|
+
id: string;
|
|
14
|
+
parentId: string | null;
|
|
15
|
+
author: string;
|
|
16
|
+
authorId?: string;
|
|
17
|
+
authorUrl?: string;
|
|
18
|
+
authorThumbnailUrl?: string;
|
|
19
|
+
authorIsUploader: boolean;
|
|
20
|
+
authorIsVerified: boolean;
|
|
21
|
+
text: string;
|
|
22
|
+
likeCount: number;
|
|
23
|
+
isPinned: boolean;
|
|
24
|
+
isFavorited: boolean;
|
|
25
|
+
timestamp?: number;
|
|
26
|
+
publishedAt?: string;
|
|
27
|
+
timeText?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface CommentThread {
|
|
30
|
+
comment: ThreadedComment;
|
|
31
|
+
replies: ThreadedComment[];
|
|
32
|
+
/** Replies present in THIS extraction, not YouTube's own figure. */
|
|
33
|
+
replyCount: number;
|
|
34
|
+
}
|
|
35
|
+
export interface ThreadedComments {
|
|
36
|
+
threads: CommentThread[];
|
|
37
|
+
rootCount: number;
|
|
38
|
+
replyCount: number;
|
|
39
|
+
/** Replies whose parent was cut by a cap. Kept, and counted, never dropped. */
|
|
40
|
+
orphanCount: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Groups rows into threads, in yt-dlp's own order.
|
|
44
|
+
*
|
|
45
|
+
* A reply whose parent is missing — because `max_parents` truncated the set —
|
|
46
|
+
* is promoted to a root rather than discarded, keeping its `parentId` so the
|
|
47
|
+
* gap stays visible. Silently dropping data is the bug this whole release is
|
|
48
|
+
* about.
|
|
49
|
+
*/
|
|
50
|
+
export declare function toThreads(rows: CommentRow[]): ThreadedComments;
|
|
51
|
+
//# sourceMappingURL=comment-threads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment-threads.d.ts","sourceRoot":"","sources":["../../src/utils/comment-threads.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;;;;GAOG;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;CACrB;AAiCD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,gBAAgB,CA0C9D"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
function toComment(row, index) {
|
|
2
|
+
const parent = row.parent ?? 'root';
|
|
3
|
+
const timestamp = row.timestamp ?? undefined;
|
|
4
|
+
// yt-dlp writes null for an absent field; normalised to undefined once here
|
|
5
|
+
// so every spread below is a plain `=== undefined` check.
|
|
6
|
+
const authorId = row.author_id ?? undefined;
|
|
7
|
+
const authorUrl = row.author_url ?? undefined;
|
|
8
|
+
const authorThumbnail = row.author_thumbnail ?? undefined;
|
|
9
|
+
const timeText = row._time_text ?? undefined;
|
|
10
|
+
return {
|
|
11
|
+
// yt-dlp always supplies an id; the fallback keeps a malformed row
|
|
12
|
+
// addressable rather than silently merging it with another.
|
|
13
|
+
id: row.id ?? `unknown-${String(index)}`,
|
|
14
|
+
parentId: parent === 'root' ? null : parent,
|
|
15
|
+
author: row.author ?? 'Unknown',
|
|
16
|
+
...(authorId === undefined ? {} : { authorId }),
|
|
17
|
+
...(authorUrl === undefined ? {} : { authorUrl }),
|
|
18
|
+
...(authorThumbnail === undefined ? {} : { authorThumbnailUrl: authorThumbnail }),
|
|
19
|
+
authorIsUploader: row.author_is_uploader ?? false,
|
|
20
|
+
authorIsVerified: row.author_is_verified ?? false,
|
|
21
|
+
text: row.text ?? '',
|
|
22
|
+
likeCount: row.like_count ?? 0,
|
|
23
|
+
isPinned: row.is_pinned ?? false,
|
|
24
|
+
isFavorited: row.is_favorited ?? false,
|
|
25
|
+
...(timestamp === undefined ? {} : { timestamp }),
|
|
26
|
+
...(timestamp === undefined ? {} : { publishedAt: new Date(timestamp * 1000).toISOString() }),
|
|
27
|
+
...(timeText === undefined ? {} : { timeText }),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Groups rows into threads, in yt-dlp's own order.
|
|
32
|
+
*
|
|
33
|
+
* A reply whose parent is missing — because `max_parents` truncated the set —
|
|
34
|
+
* is promoted to a root rather than discarded, keeping its `parentId` so the
|
|
35
|
+
* gap stays visible. Silently dropping data is the bug this whole release is
|
|
36
|
+
* about.
|
|
37
|
+
*/
|
|
38
|
+
export function toThreads(rows) {
|
|
39
|
+
const comments = rows.map(toComment);
|
|
40
|
+
const threads = [];
|
|
41
|
+
const threadById = new Map();
|
|
42
|
+
let replyCount = 0;
|
|
43
|
+
let orphanCount = 0;
|
|
44
|
+
for (const comment of comments) {
|
|
45
|
+
if (comment.parentId === null) {
|
|
46
|
+
const thread = { comment, replies: [], replyCount: 0 };
|
|
47
|
+
threads.push(thread);
|
|
48
|
+
threadById.set(comment.id, thread);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const comment of comments) {
|
|
52
|
+
if (comment.parentId === null)
|
|
53
|
+
continue;
|
|
54
|
+
const thread = threadById.get(comment.parentId);
|
|
55
|
+
if (thread === undefined) {
|
|
56
|
+
// The parent was cut by a cap. Keep the reply as its own thread so the
|
|
57
|
+
// text survives, and count it so the caller knows threads are partial.
|
|
58
|
+
orphanCount += 1;
|
|
59
|
+
threads.push({ comment, replies: [], replyCount: 0 });
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
thread.replies.push(comment);
|
|
63
|
+
thread.replyCount += 1;
|
|
64
|
+
replyCount += 1;
|
|
65
|
+
}
|
|
66
|
+
for (const thread of threads) {
|
|
67
|
+
thread.replies.sort((left, right) => (left.timestamp ?? 0) - (right.timestamp ?? 0));
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
threads,
|
|
71
|
+
rootCount: threads.length - orphanCount,
|
|
72
|
+
replyCount,
|
|
73
|
+
orphanCount,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=comment-threads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comment-threads.js","sourceRoot":"","sources":["../../src/utils/comment-threads.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage-text.d.ts","sourceRoot":"","sources":["../../src/utils/coverage-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AA8CtD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CA6BzD"}
|