youtube-knowledge-mcp 2.2.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 +62 -18
- 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/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 +2 -0
- package/dist/registry/index.js.map +1 -1
- package/dist/registry/video-tools.js +1 -1
- package/dist/registry/video-tools.js.map +1 -1
- package/dist/schemas.d.ts +239 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +56 -0
- package/dist/schemas.js.map +1 -1
- package/dist/thumbnail-schemas.d.ts +28 -28
- package/dist/tools/ask-brain.js +1 -1
- package/dist/tools/ask-brain.js.map +1 -1
- package/dist/tools/brain-info.js +1 -1
- package/dist/tools/brain-info.js.map +1 -1
- package/dist/tools/check-health.d.ts +59 -0
- package/dist/tools/check-health.d.ts.map +1 -1
- package/dist/tools/check-health.js +85 -6
- package/dist/tools/check-health.js.map +1 -1
- 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/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/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.js +1 -1
- package/dist/tools/library.js.map +1 -1
- 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.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/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/errors.d.ts +1 -1
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +9 -1
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/format.d.ts +17 -1
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +1 -1
- package/dist/utils/format.js.map +1 -1
- 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/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/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/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/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/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 +9 -2
- package/dist/utils/youtube-channel.d.ts.map +1 -1
- package/dist/utils/youtube-channel.js +33 -3
- package/dist/utils/youtube-channel.js.map +1 -1
- package/dist/utils/youtube-schemas.d.ts +98 -0
- package/dist/utils/youtube-schemas.d.ts.map +1 -1
- package/dist/utils/youtube-schemas.js +30 -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 +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"youtube-channel.d.ts","sourceRoot":"","sources":["../../src/utils/youtube-channel.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"youtube-channel.d.ts","sourceRoot":"","sources":["../../src/utils/youtube-channel.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAS3B,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,UAAU,CAAC,EAAE,WAAW,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,UAAU,CAC9B,YAAY,EAAE,MAAM,EACpB,KAAK,SAAK,EACV,KAAK,SAAI,GACR,OAAO,CAAC,aAAa,EAAE,CAAC,CAsB1B;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiBrF;AAED,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAwBhF;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAwB1E"}
|
|
@@ -1,17 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { formatYouTubeDate } from './format.js';
|
|
2
3
|
import { TIMEOUTS, parseYtDlpJson, parseYtDlpJsonLines, runYtDlp } from './ytdlp.js';
|
|
3
4
|
import { FLAT_PRINT_TEMPLATE, flatEntrySchema, toVideoListItem, } from './flat-listing.js';
|
|
4
5
|
import { selectChannelImages } from './channel-images.js';
|
|
5
6
|
import { channelMetaSchema, playlistMetaSchema } from './youtube-schemas.js';
|
|
6
7
|
import { channelUrlFor, resolveListTarget } from './youtube-url.js';
|
|
7
|
-
|
|
8
|
+
/** Listings and metadata for channels and playlists. */
|
|
9
|
+
const playlistCountSchema = z.object({ playlist_count: z.number().nullish() });
|
|
10
|
+
export async function listVideos(urlOrChannel, limit = 20, start = 1) {
|
|
8
11
|
const stdout = await runYtDlp([
|
|
9
12
|
'--skip-download',
|
|
10
13
|
'--flat-playlist',
|
|
14
|
+
'--lazy-playlist',
|
|
11
15
|
'--print',
|
|
12
16
|
FLAT_PRINT_TEMPLATE,
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
// `-I start:end` rather than --playlist-end so a later page can begin
|
|
18
|
+
// where the previous one stopped. YouTube still walks from the top to
|
|
19
|
+
// reach `start`, which is why the caller caches instead of paging deep.
|
|
20
|
+
'--playlist-items',
|
|
21
|
+
`${String(start)}:${String(start + limit - 1)}`,
|
|
15
22
|
], {
|
|
16
23
|
label: 'fetch_videos',
|
|
17
24
|
timeoutMs: TIMEOUTS.transcript,
|
|
@@ -19,6 +26,29 @@ export async function listVideos(urlOrChannel, limit = 20) {
|
|
|
19
26
|
});
|
|
20
27
|
return parseYtDlpJsonLines(stdout, flatEntrySchema).map(toVideoListItem);
|
|
21
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* How many videos a listing says it has, when it says anything.
|
|
31
|
+
*
|
|
32
|
+
* `playlist_count` is populated for playlists and routinely null for channel
|
|
33
|
+
* tabs, so this returns undefined rather than a guess. One extra cheap call —
|
|
34
|
+
* the same `--playlist-items 0` metadata read getPlaylistInfo already does.
|
|
35
|
+
*/
|
|
36
|
+
export async function playlistTotal(urlOrChannel) {
|
|
37
|
+
try {
|
|
38
|
+
const stdout = await runYtDlp(['--dump-single-json', '--flat-playlist', '--playlist-items', '0'], {
|
|
39
|
+
label: 'fetch_videos:count',
|
|
40
|
+
timeoutMs: TIMEOUTS.metadata,
|
|
41
|
+
target: resolveListTarget(urlOrChannel),
|
|
42
|
+
});
|
|
43
|
+
const parsed = parseYtDlpJson(stdout, playlistCountSchema, 'a playlist count');
|
|
44
|
+
return parsed.playlist_count ?? undefined;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// A total is a nicety; failing to get one must not fail the page. Its
|
|
48
|
+
// absence is already meaningful — "unknown", which is the truth here.
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
22
52
|
export async function getPlaylistInfo(playlistUrl) {
|
|
23
53
|
const target = resolveListTarget(playlistUrl);
|
|
24
54
|
const stdout = await runYtDlp(['--dump-single-json', '--flat-playlist', '--playlist-items', '0'], {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"youtube-channel.js","sourceRoot":"","sources":["../../src/utils/youtube-channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACrF,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,eAAe,GAEhB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"youtube-channel.js","sourceRoot":"","sources":["../../src/utils/youtube-channel.ts"],"names":[],"mappings":"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"}
|
|
@@ -92,9 +92,16 @@ export declare const videoDetailsRowSchema: z.ZodObject<{
|
|
|
92
92
|
}>, "many">>>;
|
|
93
93
|
upload_date: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
94
94
|
duration: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
95
|
+
/**
|
|
96
|
+
* The video's real comment count — trustworthy ONLY here, because this read
|
|
97
|
+
* does not pass --write-comments. With that flag yt-dlp replaces the value
|
|
98
|
+
* with the number it extracted (measured: 300 against a video with 2.4M).
|
|
99
|
+
*/
|
|
100
|
+
comment_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
95
101
|
}, "strip", z.ZodTypeAny, {
|
|
96
102
|
duration?: number | null | undefined;
|
|
97
103
|
upload_date?: string | null | undefined;
|
|
104
|
+
comment_count?: number | null | undefined;
|
|
98
105
|
chapters?: {
|
|
99
106
|
title: string;
|
|
100
107
|
start_time: number;
|
|
@@ -103,66 +110,157 @@ export declare const videoDetailsRowSchema: z.ZodObject<{
|
|
|
103
110
|
}, {
|
|
104
111
|
duration?: number | null | undefined;
|
|
105
112
|
upload_date?: string | null | undefined;
|
|
113
|
+
comment_count?: number | null | undefined;
|
|
106
114
|
chapters?: {
|
|
107
115
|
title: string;
|
|
108
116
|
start_time: number;
|
|
109
117
|
end_time: number;
|
|
110
118
|
}[] | null | undefined;
|
|
111
119
|
}>;
|
|
120
|
+
/**
|
|
121
|
+
* Every field yt-dlp returns for a comment, verified against 2026.08.19.
|
|
122
|
+
*
|
|
123
|
+
* Nine of these were previously dropped on the floor after being paid for:
|
|
124
|
+
* the extraction fetches them either way, so discarding `id` and `parent`
|
|
125
|
+
* meant a thread could not be rebuilt from what we already had.
|
|
126
|
+
*/
|
|
112
127
|
export declare const commentRowSchema: z.ZodObject<{
|
|
128
|
+
id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
113
129
|
author: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
130
|
+
author_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
131
|
+
author_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
132
|
+
author_thumbnail: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
133
|
+
author_is_uploader: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
134
|
+
author_is_verified: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
114
135
|
text: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
115
136
|
like_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
116
137
|
is_pinned: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
138
|
+
is_favorited: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
139
|
+
timestamp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
140
|
+
_time_text: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
141
|
+
/** 'root' for a top-level comment; the parent's id for a reply. */
|
|
117
142
|
parent: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
118
143
|
}, "strip", z.ZodTypeAny, {
|
|
144
|
+
id?: string | null | undefined;
|
|
119
145
|
author?: string | null | undefined;
|
|
120
146
|
text?: string | null | undefined;
|
|
147
|
+
timestamp?: number | null | undefined;
|
|
121
148
|
parent?: string | null | undefined;
|
|
122
149
|
like_count?: number | null | undefined;
|
|
150
|
+
author_id?: string | null | undefined;
|
|
151
|
+
author_url?: string | null | undefined;
|
|
152
|
+
author_thumbnail?: string | null | undefined;
|
|
153
|
+
author_is_uploader?: boolean | null | undefined;
|
|
154
|
+
author_is_verified?: boolean | null | undefined;
|
|
123
155
|
is_pinned?: boolean | null | undefined;
|
|
156
|
+
is_favorited?: boolean | null | undefined;
|
|
157
|
+
_time_text?: string | null | undefined;
|
|
124
158
|
}, {
|
|
159
|
+
id?: string | null | undefined;
|
|
125
160
|
author?: string | null | undefined;
|
|
126
161
|
text?: string | null | undefined;
|
|
162
|
+
timestamp?: number | null | undefined;
|
|
127
163
|
parent?: string | null | undefined;
|
|
128
164
|
like_count?: number | null | undefined;
|
|
165
|
+
author_id?: string | null | undefined;
|
|
166
|
+
author_url?: string | null | undefined;
|
|
167
|
+
author_thumbnail?: string | null | undefined;
|
|
168
|
+
author_is_uploader?: boolean | null | undefined;
|
|
169
|
+
author_is_verified?: boolean | null | undefined;
|
|
129
170
|
is_pinned?: boolean | null | undefined;
|
|
171
|
+
is_favorited?: boolean | null | undefined;
|
|
172
|
+
_time_text?: string | null | undefined;
|
|
130
173
|
}>;
|
|
131
174
|
export declare const commentsRowSchema: z.ZodObject<{
|
|
132
175
|
comments: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
176
|
+
id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
133
177
|
author: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
178
|
+
author_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
179
|
+
author_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
180
|
+
author_thumbnail: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
181
|
+
author_is_uploader: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
182
|
+
author_is_verified: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
134
183
|
text: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
135
184
|
like_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
136
185
|
is_pinned: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
186
|
+
is_favorited: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
187
|
+
timestamp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
188
|
+
_time_text: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
189
|
+
/** 'root' for a top-level comment; the parent's id for a reply. */
|
|
137
190
|
parent: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
138
191
|
}, "strip", z.ZodTypeAny, {
|
|
192
|
+
id?: string | null | undefined;
|
|
139
193
|
author?: string | null | undefined;
|
|
140
194
|
text?: string | null | undefined;
|
|
195
|
+
timestamp?: number | null | undefined;
|
|
141
196
|
parent?: string | null | undefined;
|
|
142
197
|
like_count?: number | null | undefined;
|
|
198
|
+
author_id?: string | null | undefined;
|
|
199
|
+
author_url?: string | null | undefined;
|
|
200
|
+
author_thumbnail?: string | null | undefined;
|
|
201
|
+
author_is_uploader?: boolean | null | undefined;
|
|
202
|
+
author_is_verified?: boolean | null | undefined;
|
|
143
203
|
is_pinned?: boolean | null | undefined;
|
|
204
|
+
is_favorited?: boolean | null | undefined;
|
|
205
|
+
_time_text?: string | null | undefined;
|
|
144
206
|
}, {
|
|
207
|
+
id?: string | null | undefined;
|
|
145
208
|
author?: string | null | undefined;
|
|
146
209
|
text?: string | null | undefined;
|
|
210
|
+
timestamp?: number | null | undefined;
|
|
147
211
|
parent?: string | null | undefined;
|
|
148
212
|
like_count?: number | null | undefined;
|
|
213
|
+
author_id?: string | null | undefined;
|
|
214
|
+
author_url?: string | null | undefined;
|
|
215
|
+
author_thumbnail?: string | null | undefined;
|
|
216
|
+
author_is_uploader?: boolean | null | undefined;
|
|
217
|
+
author_is_verified?: boolean | null | undefined;
|
|
149
218
|
is_pinned?: boolean | null | undefined;
|
|
219
|
+
is_favorited?: boolean | null | undefined;
|
|
220
|
+
_time_text?: string | null | undefined;
|
|
150
221
|
}>, "many">>>;
|
|
222
|
+
/**
|
|
223
|
+
* With --write-comments yt-dlp OVERWRITES this with the number extracted,
|
|
224
|
+
* and sets it to null when the extraction was interrupted. So it is a
|
|
225
|
+
* three-state signal about this run, never the video's real total, which
|
|
226
|
+
* only a separate metadata pass can report.
|
|
227
|
+
*/
|
|
228
|
+
comment_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
151
229
|
}, "strip", z.ZodTypeAny, {
|
|
230
|
+
comment_count?: number | null | undefined;
|
|
152
231
|
comments?: {
|
|
232
|
+
id?: string | null | undefined;
|
|
153
233
|
author?: string | null | undefined;
|
|
154
234
|
text?: string | null | undefined;
|
|
235
|
+
timestamp?: number | null | undefined;
|
|
155
236
|
parent?: string | null | undefined;
|
|
156
237
|
like_count?: number | null | undefined;
|
|
238
|
+
author_id?: string | null | undefined;
|
|
239
|
+
author_url?: string | null | undefined;
|
|
240
|
+
author_thumbnail?: string | null | undefined;
|
|
241
|
+
author_is_uploader?: boolean | null | undefined;
|
|
242
|
+
author_is_verified?: boolean | null | undefined;
|
|
157
243
|
is_pinned?: boolean | null | undefined;
|
|
244
|
+
is_favorited?: boolean | null | undefined;
|
|
245
|
+
_time_text?: string | null | undefined;
|
|
158
246
|
}[] | null | undefined;
|
|
159
247
|
}, {
|
|
248
|
+
comment_count?: number | null | undefined;
|
|
160
249
|
comments?: {
|
|
250
|
+
id?: string | null | undefined;
|
|
161
251
|
author?: string | null | undefined;
|
|
162
252
|
text?: string | null | undefined;
|
|
253
|
+
timestamp?: number | null | undefined;
|
|
163
254
|
parent?: string | null | undefined;
|
|
164
255
|
like_count?: number | null | undefined;
|
|
256
|
+
author_id?: string | null | undefined;
|
|
257
|
+
author_url?: string | null | undefined;
|
|
258
|
+
author_thumbnail?: string | null | undefined;
|
|
259
|
+
author_is_uploader?: boolean | null | undefined;
|
|
260
|
+
author_is_verified?: boolean | null | undefined;
|
|
165
261
|
is_pinned?: boolean | null | undefined;
|
|
262
|
+
is_favorited?: boolean | null | undefined;
|
|
263
|
+
_time_text?: string | null | undefined;
|
|
166
264
|
}[] | null | undefined;
|
|
167
265
|
}>;
|
|
168
266
|
export declare const formatRowSchema: z.ZodObject<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"youtube-schemas.d.ts","sourceRoot":"","sources":["../../src/utils/youtube-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AAEH,2FAA2F;AAC3F,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgB7B,CAAC;AAEH,wFAAwF;AACxF,eAAO,MAAM,kBAAkB;;;;;;;;;EAG7B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;EAI3B,CAAC;AAEH,eAAO,MAAM,qBAAqB
|
|
1
|
+
{"version":3,"file":"youtube-schemas.d.ts","sourceRoot":"","sources":["../../src/utils/youtube-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AAEH,2FAA2F;AAC3F,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgB7B,CAAC;AAEH,wFAAwF;AACxF,eAAO,MAAM,kBAAkB;;;;;;;;;EAG7B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;EAI3B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;IAIhC;;;;OAIG;;;;;;;;;;;;;;;;;;;;EAEH,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;IAc3B,mEAAmE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEnE,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;QAJ5B,mEAAmE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAMnE;;;;;OAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEH,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAY1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAE3B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;EAG9B,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;EAO7B,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;EASjC,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;IAO5B,mEAAmE;;;;;;;;;;;;;;;;;;EAEnE,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAU7B,CAAC"}
|
|
@@ -40,16 +40,46 @@ export const videoDetailsRowSchema = z.object({
|
|
|
40
40
|
chapters: z.array(chapterRowSchema).nullish(),
|
|
41
41
|
upload_date: z.string().nullish(),
|
|
42
42
|
duration: z.number().nullish(),
|
|
43
|
+
/**
|
|
44
|
+
* The video's real comment count — trustworthy ONLY here, because this read
|
|
45
|
+
* does not pass --write-comments. With that flag yt-dlp replaces the value
|
|
46
|
+
* with the number it extracted (measured: 300 against a video with 2.4M).
|
|
47
|
+
*/
|
|
48
|
+
comment_count: z.number().nullish(),
|
|
43
49
|
});
|
|
50
|
+
/**
|
|
51
|
+
* Every field yt-dlp returns for a comment, verified against 2026.08.19.
|
|
52
|
+
*
|
|
53
|
+
* Nine of these were previously dropped on the floor after being paid for:
|
|
54
|
+
* the extraction fetches them either way, so discarding `id` and `parent`
|
|
55
|
+
* meant a thread could not be rebuilt from what we already had.
|
|
56
|
+
*/
|
|
44
57
|
export const commentRowSchema = z.object({
|
|
58
|
+
id: z.string().nullish(),
|
|
45
59
|
author: z.string().nullish(),
|
|
60
|
+
author_id: z.string().nullish(),
|
|
61
|
+
author_url: z.string().nullish(),
|
|
62
|
+
author_thumbnail: z.string().nullish(),
|
|
63
|
+
author_is_uploader: z.boolean().nullish(),
|
|
64
|
+
author_is_verified: z.boolean().nullish(),
|
|
46
65
|
text: z.string().nullish(),
|
|
47
66
|
like_count: z.number().nullish(),
|
|
48
67
|
is_pinned: z.boolean().nullish(),
|
|
68
|
+
is_favorited: z.boolean().nullish(),
|
|
69
|
+
timestamp: z.number().nullish(),
|
|
70
|
+
_time_text: z.string().nullish(),
|
|
71
|
+
/** 'root' for a top-level comment; the parent's id for a reply. */
|
|
49
72
|
parent: z.string().nullish(),
|
|
50
73
|
});
|
|
51
74
|
export const commentsRowSchema = z.object({
|
|
52
75
|
comments: z.array(commentRowSchema).nullish(),
|
|
76
|
+
/**
|
|
77
|
+
* With --write-comments yt-dlp OVERWRITES this with the number extracted,
|
|
78
|
+
* and sets it to null when the extraction was interrupted. So it is a
|
|
79
|
+
* three-state signal about this run, never the video's real total, which
|
|
80
|
+
* only a separate metadata pass can report.
|
|
81
|
+
*/
|
|
82
|
+
comment_count: z.number().nullish(),
|
|
53
83
|
});
|
|
54
84
|
export const formatRowSchema = z.object({
|
|
55
85
|
format_id: z.string(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"youtube-schemas.js","sourceRoot":"","sources":["../../src/utils/youtube-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AAEH,2FAA2F;AAC3F,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACxB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC3B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC7B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC9B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,4EAA4E;IAC5E,0DAA0D;IAC1D,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE;IACjB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC/B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAChC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAChC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACnC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAClC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;CAClC,CAAC,CAAC;AAEH,wFAAwF;AACxF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC3B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;CAC/B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;CACrB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,EAAE;IAC7C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;
|
|
1
|
+
{"version":3,"file":"youtube-schemas.js","sourceRoot":"","sources":["../../src/utils/youtube-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;GAQG;AAEH,2FAA2F;AAC3F,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACxB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC3B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC7B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC9B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,4EAA4E;IAC5E,0DAA0D;IAC1D,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE;IACjB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC/B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAChC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAChC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACnC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAClC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;CAClC,CAAC,CAAC;AAEH,wFAAwF;AACxF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC3B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;CAC/B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;IACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;CACrB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,EAAE;IAC7C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC9B;;;;OAIG;IACH,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;CACpC,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACxB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC5B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC/B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAChC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACtC,kBAAkB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE;IACzC,kBAAkB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC1B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAChC,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE;IAChC,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE;IACnC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC/B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAChC,mEAAmE;IACnE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;CAC7B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,OAAO,EAAE;IAC7C;;;;;OAKG;IACH,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;CACpC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAChC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC3B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC5B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACzB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC5B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC5B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC9B,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACrC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;CAClC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,OAAO,EAAE;CAC5C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;IACtD,kBAAkB,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;CAChE,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC3B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC9B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC7B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAChC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;CAC1B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACxB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC3B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC7B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAChC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,sBAAsB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC5C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;CAClC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC7B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAChC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,sBAAsB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC5C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,mEAAmE;IACnE,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE;CACxB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACxB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC3B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC7B,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACpC,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACnC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IACjC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;CAClC,CAAC,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ThreadedComments } from './comment-threads.js';
|
|
1
2
|
/** Everything read about one video: metadata, chapters and comments. */
|
|
2
3
|
export interface VideoInfo {
|
|
3
4
|
id: string;
|
|
@@ -41,7 +42,27 @@ export interface VideoDetails {
|
|
|
41
42
|
uploadDate: string;
|
|
42
43
|
durationSeconds: number;
|
|
43
44
|
chapters: Chapter[];
|
|
45
|
+
/** YouTube's own count, from a read without --write-comments. Approximate and lagging. */
|
|
46
|
+
commentCount?: number;
|
|
44
47
|
}
|
|
45
48
|
export declare function getVideoDetails(urlOrId: string): Promise<VideoDetails>;
|
|
46
|
-
export
|
|
49
|
+
export interface CommentsResult extends ThreadedComments {
|
|
50
|
+
/**
|
|
51
|
+
* Whether yt-dlp's own iteration finished rather than being cut off.
|
|
52
|
+
*
|
|
53
|
+
* From common.py:3882-3908: `comment_count` is overwritten with the number
|
|
54
|
+
* extracted, and set to null when the walk was interrupted. Necessary for
|
|
55
|
+
* completeness but NOT sufficient — `itertools.islice` also ends iteration
|
|
56
|
+
* at a cap, so a capped run reports that it finished too.
|
|
57
|
+
*/
|
|
58
|
+
ranToExhaustion: boolean;
|
|
59
|
+
/** True when the video has comments turned off, where zero is the real total. */
|
|
60
|
+
commentsDisabled: boolean;
|
|
61
|
+
extractedTotal: number;
|
|
62
|
+
}
|
|
63
|
+
export declare function getComments(urlOrId: string, options?: {
|
|
64
|
+
limit?: number;
|
|
65
|
+
sort?: 'top' | 'new';
|
|
66
|
+
maxRepliesPerThread?: number;
|
|
67
|
+
}): Promise<CommentsResult>;
|
|
47
68
|
//# sourceMappingURL=youtube-video.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"youtube-video.d.ts","sourceRoot":"","sources":["../../src/utils/youtube-video.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"youtube-video.d.ts","sourceRoot":"","sources":["../../src/utils/youtube-video.ts"],"names":[],"mappings":"AAKA,OAAO,EAAa,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxE,wEAAwE;AAExE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAKD,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAoDtE;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAErE;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,0FAA0F;IAC1F,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAkB5E;AAED,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD;;;;;;;OAOG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB,iFAAiF;IACjF,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAAC,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAAO,GACnF,OAAO,CAAC,cAAc,CAAC,CAgCzB"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { formatYouTubeDate } from './format.js';
|
|
2
2
|
import { classifyPlayability } from './errors.js';
|
|
3
3
|
import { TIMEOUTS, parseYtDlpJson, runYtDlp } from './ytdlp.js';
|
|
4
|
-
import { commentsRowSchema,
|
|
4
|
+
import { commentsRowSchema, videoInfoRowSchema } from './youtube-schemas.js';
|
|
5
5
|
import { extractVideoId, formatDuration, watchUrl } from './youtube-url.js';
|
|
6
|
+
import { toThreads } from './comment-threads.js';
|
|
7
|
+
import { readVideoStats } from './video-stats-cache.js';
|
|
6
8
|
const VIDEO_INFO_TEMPLATE = '%(.{id,title,channel,duration,upload_date,description,tags,thumbnail,view_count,like_count,comment_count,availability,live_status})j';
|
|
7
9
|
export async function getVideoInfo(urlOrId) {
|
|
8
10
|
const videoId = extractVideoId(urlOrId);
|
|
@@ -54,26 +56,26 @@ export async function getChapters(urlOrId) {
|
|
|
54
56
|
return (await getVideoDetails(urlOrId)).chapters;
|
|
55
57
|
}
|
|
56
58
|
export async function getVideoDetails(urlOrId) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
target: url,
|
|
62
|
-
});
|
|
63
|
-
const data = parseYtDlpJson(stdout, videoDetailsRowSchema, 'video details');
|
|
59
|
+
// A projection of the one full read, not a second spawn. This used to run
|
|
60
|
+
// its own `-j` and discard everything but three fields, so a caller wanting
|
|
61
|
+
// a title and a chapter list paid for two extractions of the same video.
|
|
62
|
+
const { stats } = await readVideoStats(urlOrId);
|
|
64
63
|
return {
|
|
65
|
-
uploadDate:
|
|
66
|
-
durationSeconds:
|
|
67
|
-
chapters:
|
|
68
|
-
title:
|
|
69
|
-
startTime:
|
|
70
|
-
startTimeFormatted: formatDuration(Math.floor(
|
|
71
|
-
endTime:
|
|
72
|
-
endTimeFormatted: formatDuration(Math.floor(
|
|
64
|
+
uploadDate: stats.uploadDate,
|
|
65
|
+
durationSeconds: stats.durationSeconds,
|
|
66
|
+
chapters: stats.chapters.map((chapter) => ({
|
|
67
|
+
title: chapter.title,
|
|
68
|
+
startTime: chapter.startTime,
|
|
69
|
+
startTimeFormatted: formatDuration(Math.floor(chapter.startTime)),
|
|
70
|
+
endTime: chapter.endTime,
|
|
71
|
+
endTimeFormatted: formatDuration(Math.floor(chapter.endTime)),
|
|
73
72
|
})),
|
|
73
|
+
...(stats.commentCount === undefined ? {} : { commentCount: stats.commentCount }),
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
|
-
export async function getComments(urlOrId,
|
|
76
|
+
export async function getComments(urlOrId, options = {}) {
|
|
77
|
+
const limit = options.limit ?? 20;
|
|
78
|
+
const sort = options.sort ?? 'top';
|
|
77
79
|
const videoId = extractVideoId(urlOrId);
|
|
78
80
|
const url = watchUrl(videoId);
|
|
79
81
|
const stdout = await runYtDlp([
|
|
@@ -81,18 +83,28 @@ export async function getComments(urlOrId, limit = 20) {
|
|
|
81
83
|
'--skip-download',
|
|
82
84
|
'--write-comments',
|
|
83
85
|
'--extractor-args',
|
|
84
|
-
|
|
86
|
+
// Five positions, per _video.py:2578: total, parents, replies, per
|
|
87
|
+
// thread, depth. An omitted position means unbounded, not a default, so
|
|
88
|
+
// every one is stated. Depth is 2 because YouTube's tree is two levels
|
|
89
|
+
// and a deeper cap would spend requests on nothing.
|
|
90
|
+
youtubeCommentArgs({ limit, sort, maxRepliesPerThread: options.maxRepliesPerThread ?? 100 }),
|
|
85
91
|
], { label: 'get_comments', timeoutMs: TIMEOUTS.comments, target: url });
|
|
86
92
|
const data = parseYtDlpJson(stdout, commentsRowSchema, 'video comments');
|
|
87
|
-
const
|
|
88
|
-
return
|
|
89
|
-
|
|
90
|
-
.
|
|
91
|
-
.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
const rows = data.comments ?? [];
|
|
94
|
+
return {
|
|
95
|
+
...toThreads(rows),
|
|
96
|
+
ranToExhaustion: data.comment_count !== null && data.comment_count !== undefined,
|
|
97
|
+
commentsDisabled: (data.comments === null || data.comments === undefined) &&
|
|
98
|
+
(data.comment_count === null || data.comment_count === undefined),
|
|
99
|
+
extractedTotal: rows.length,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function youtubeCommentArgs(options) {
|
|
103
|
+
const parents = Math.max(1, Math.ceil(options.limit / 2));
|
|
104
|
+
const replies = Math.max(0, options.limit - parents);
|
|
105
|
+
return [
|
|
106
|
+
`youtube:comment_sort=${options.sort}`,
|
|
107
|
+
`max_comments=${String(options.limit)},${String(parents)},${String(replies)},${String(options.maxRepliesPerThread)},2`,
|
|
108
|
+
].join(';');
|
|
97
109
|
}
|
|
98
110
|
//# sourceMappingURL=youtube-video.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"youtube-video.js","sourceRoot":"","sources":["../../src/utils/youtube-video.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"youtube-video.js","sourceRoot":"","sources":["../../src/utils/youtube-video.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,6 +19,9 @@ export declare function releaseSlot(): void;
|
|
|
19
19
|
export declare function concurrencyState(): {
|
|
20
20
|
active: number;
|
|
21
21
|
queued: number;
|
|
22
|
+
/** The level in force now, which throttling can lower. */
|
|
22
23
|
limit: number;
|
|
24
|
+
/** The configured maximum the level never exceeds. */
|
|
25
|
+
ceiling: number;
|
|
23
26
|
};
|
|
24
27
|
//# sourceMappingURL=ytdlp-limiter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ytdlp-limiter.d.ts","sourceRoot":"","sources":["../../src/utils/ytdlp-limiter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ytdlp-limiter.d.ts","sourceRoot":"","sources":["../../src/utils/ytdlp-limiter.ts"],"names":[],"mappings":"AAiCA;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhF;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAOlC;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,IAAI;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;CACjB,CAOA"}
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { envInt } from './env.js';
|
|
2
2
|
import { YouTubeError } from './errors.js';
|
|
3
|
+
import { effectiveConcurrency } from './ytdlp-pacer.js';
|
|
3
4
|
/**
|
|
4
5
|
* The concurrency limiter every yt-dlp spawn passes through.
|
|
5
6
|
*
|
|
6
7
|
* A cap so batch operations do not look like a scraper to YouTube, with a queue
|
|
7
8
|
* that honours cancellation so an abandoned request never holds a place in it.
|
|
8
9
|
*/
|
|
9
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* The ceiling this server will never exceed.
|
|
12
|
+
*
|
|
13
|
+
* A limit of 0 (or NaN) would queue every call forever, so the floor is 1.
|
|
14
|
+
* Under throttling the pacer lowers the level below this; the setting is the
|
|
15
|
+
* maximum, not the level.
|
|
16
|
+
*/
|
|
10
17
|
const MAX_CONCURRENT = envInt('YOUTUBE_MCP_MAX_CONCURRENCY', 3, { min: 1, max: 32 });
|
|
18
|
+
/** The level in force right now: the configured ceiling, or less if throttled. */
|
|
19
|
+
function currentLimit() {
|
|
20
|
+
return Math.max(1, Math.min(MAX_CONCURRENT, effectiveConcurrency()));
|
|
21
|
+
}
|
|
11
22
|
let active = 0;
|
|
12
23
|
const waiting = [];
|
|
13
24
|
/**
|
|
@@ -23,7 +34,7 @@ export async function acquireSlot(signal) {
|
|
|
23
34
|
// No already-aborted check here: `runYtDlp` makes it before every attempt and
|
|
24
35
|
// nothing awaits in between, so a second one could never fire — an
|
|
25
36
|
// unreachable guard that reads like a reachable one.
|
|
26
|
-
if (active <
|
|
37
|
+
if (active < currentLimit()) {
|
|
27
38
|
active++;
|
|
28
39
|
return;
|
|
29
40
|
}
|
|
@@ -64,6 +75,11 @@ export function releaseSlot() {
|
|
|
64
75
|
}
|
|
65
76
|
/** Test seam: lets the suite assert the limiter without spawning processes. */
|
|
66
77
|
export function concurrencyState() {
|
|
67
|
-
return {
|
|
78
|
+
return {
|
|
79
|
+
active,
|
|
80
|
+
queued: waiting.length,
|
|
81
|
+
limit: currentLimit(),
|
|
82
|
+
ceiling: MAX_CONCURRENT,
|
|
83
|
+
};
|
|
68
84
|
}
|
|
69
85
|
//# sourceMappingURL=ytdlp-limiter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ytdlp-limiter.js","sourceRoot":"","sources":["../../src/utils/ytdlp-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ytdlp-limiter.js","sourceRoot":"","sources":["../../src/utils/ytdlp-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD;;;;;GAKG;AAEH;;;;;;GAMG;AACH,MAAM,cAAc,GAAG,MAAM,CAAC,6BAA6B,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;AAErF,kFAAkF;AAClF,SAAS,YAAY;IACnB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC;AACvE,CAAC;AAOD,IAAI,MAAM,GAAG,CAAC,CAAC;AACf,MAAM,OAAO,GAAa,EAAE,CAAC;AAE7B;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAA+B;IAC/D,8EAA8E;IAC9E,mEAAmE;IACnE,qDAAqD;IACrD,IAAI,MAAM,GAAG,YAAY,EAAE,EAAE,CAAC;QAC5B,MAAM,EAAE,CAAC;QACT,OAAO;IACT,CAAC;IAED,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,MAAM,GAAW;YACrB,KAAK,EAAE,GAAG,EAAE;gBACV,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC9C,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;gBAChB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC9C,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;SACF,CAAC;QAEF,SAAS,OAAO;YACd,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACtC,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAC3C,MAAM,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,WAAW,EAAE,4BAA4B,CAAC,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3D,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAC7B,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO;IACT,CAAC;IACD,MAAM,EAAE,CAAC;AACX,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB;IAQ9B,OAAO;QACL,MAAM;QACN,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,YAAY,EAAE;QACrB,OAAO,EAAE,cAAc;KACxB,CAAC;AACJ,CAAC"}
|