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.
Files changed (192) hide show
  1. package/README.md +62 -18
  2. package/dist/harvest-schemas.d.ts +75 -0
  3. package/dist/harvest-schemas.d.ts.map +1 -0
  4. package/dist/harvest-schemas.js +106 -0
  5. package/dist/harvest-schemas.js.map +1 -0
  6. package/dist/registry/harvest-tools.d.ts +10 -0
  7. package/dist/registry/harvest-tools.d.ts.map +1 -0
  8. package/dist/registry/harvest-tools.js +109 -0
  9. package/dist/registry/harvest-tools.js.map +1 -0
  10. package/dist/registry/health-tools.d.ts.map +1 -1
  11. package/dist/registry/health-tools.js +18 -0
  12. package/dist/registry/health-tools.js.map +1 -1
  13. package/dist/registry/index.d.ts.map +1 -1
  14. package/dist/registry/index.js +2 -0
  15. package/dist/registry/index.js.map +1 -1
  16. package/dist/registry/video-tools.js +1 -1
  17. package/dist/registry/video-tools.js.map +1 -1
  18. package/dist/schemas.d.ts +239 -0
  19. package/dist/schemas.d.ts.map +1 -1
  20. package/dist/schemas.js +56 -0
  21. package/dist/schemas.js.map +1 -1
  22. package/dist/thumbnail-schemas.d.ts +28 -28
  23. package/dist/tools/ask-brain.js +1 -1
  24. package/dist/tools/ask-brain.js.map +1 -1
  25. package/dist/tools/brain-info.js +1 -1
  26. package/dist/tools/brain-info.js.map +1 -1
  27. package/dist/tools/check-health.d.ts +59 -0
  28. package/dist/tools/check-health.d.ts.map +1 -1
  29. package/dist/tools/check-health.js +85 -6
  30. package/dist/tools/check-health.js.map +1 -1
  31. package/dist/tools/fetch-channel-thumbnails.d.ts +8 -8
  32. package/dist/tools/fetch-videos.d.ts +15 -4
  33. package/dist/tools/fetch-videos.d.ts.map +1 -1
  34. package/dist/tools/fetch-videos.js +63 -22
  35. package/dist/tools/fetch-videos.js.map +1 -1
  36. package/dist/tools/get-comments.d.ts +238 -6
  37. package/dist/tools/get-comments.d.ts.map +1 -1
  38. package/dist/tools/get-comments.js +85 -21
  39. package/dist/tools/get-comments.js.map +1 -1
  40. package/dist/tools/get-coverage.d.ts +122 -0
  41. package/dist/tools/get-coverage.d.ts.map +1 -0
  42. package/dist/tools/get-coverage.js +105 -0
  43. package/dist/tools/get-coverage.js.map +1 -0
  44. package/dist/tools/harvest-channel.d.ts +92 -0
  45. package/dist/tools/harvest-channel.d.ts.map +1 -0
  46. package/dist/tools/harvest-channel.js +62 -0
  47. package/dist/tools/harvest-channel.js.map +1 -0
  48. package/dist/tools/harvest-comments.d.ts +81 -0
  49. package/dist/tools/harvest-comments.d.ts.map +1 -0
  50. package/dist/tools/harvest-comments.js +115 -0
  51. package/dist/tools/harvest-comments.js.map +1 -0
  52. package/dist/tools/library.js +1 -1
  53. package/dist/tools/library.js.map +1 -1
  54. package/dist/tools/list-channel-thumbnails.d.ts +10 -10
  55. package/dist/tools/list-channel-thumbnails.js +1 -1
  56. package/dist/tools/list-channel-thumbnails.js.map +1 -1
  57. package/dist/tools/list-library.js +1 -1
  58. package/dist/tools/list-library.js.map +1 -1
  59. package/dist/tools/prune-harvest.d.ts +59 -0
  60. package/dist/tools/prune-harvest.d.ts.map +1 -0
  61. package/dist/tools/prune-harvest.js +108 -0
  62. package/dist/tools/prune-harvest.js.map +1 -0
  63. package/dist/tools/query-comments.d.ts +125 -0
  64. package/dist/tools/query-comments.d.ts.map +1 -0
  65. package/dist/tools/query-comments.js +106 -0
  66. package/dist/tools/query-comments.js.map +1 -0
  67. package/dist/tools/query-videos.d.ts +118 -0
  68. package/dist/tools/query-videos.d.ts.map +1 -0
  69. package/dist/tools/query-videos.js +97 -0
  70. package/dist/tools/query-videos.js.map +1 -0
  71. package/dist/tools/repair-store.d.ts +34 -0
  72. package/dist/tools/repair-store.d.ts.map +1 -0
  73. package/dist/tools/repair-store.js +58 -0
  74. package/dist/tools/repair-store.js.map +1 -0
  75. package/dist/tools/search-channels.d.ts.map +1 -1
  76. package/dist/tools/search-channels.js +5 -1
  77. package/dist/tools/search-channels.js.map +1 -1
  78. package/dist/tools/search-videos.js +1 -1
  79. package/dist/tools/search-videos.js.map +1 -1
  80. package/dist/utils/comment-store.d.ts +71 -0
  81. package/dist/utils/comment-store.d.ts.map +1 -0
  82. package/dist/utils/comment-store.js +129 -0
  83. package/dist/utils/comment-store.js.map +1 -0
  84. package/dist/utils/comment-threads.d.ts +51 -0
  85. package/dist/utils/comment-threads.d.ts.map +1 -0
  86. package/dist/utils/comment-threads.js +76 -0
  87. package/dist/utils/comment-threads.js.map +1 -0
  88. package/dist/utils/coverage-text.d.ts +3 -0
  89. package/dist/utils/coverage-text.d.ts.map +1 -0
  90. package/dist/utils/coverage-text.js +62 -0
  91. package/dist/utils/coverage-text.js.map +1 -0
  92. package/dist/utils/coverage.d.ts +57 -0
  93. package/dist/utils/coverage.d.ts.map +1 -0
  94. package/dist/utils/coverage.js +142 -0
  95. package/dist/utils/coverage.js.map +1 -0
  96. package/dist/utils/errors.d.ts +1 -1
  97. package/dist/utils/errors.d.ts.map +1 -1
  98. package/dist/utils/errors.js +9 -1
  99. package/dist/utils/errors.js.map +1 -1
  100. package/dist/utils/format.d.ts +17 -1
  101. package/dist/utils/format.d.ts.map +1 -1
  102. package/dist/utils/format.js +1 -1
  103. package/dist/utils/format.js.map +1 -1
  104. package/dist/utils/harvest-catalog.d.ts +44 -0
  105. package/dist/utils/harvest-catalog.d.ts.map +1 -0
  106. package/dist/utils/harvest-catalog.js +164 -0
  107. package/dist/utils/harvest-catalog.js.map +1 -0
  108. package/dist/utils/harvest-lock.d.ts +3 -0
  109. package/dist/utils/harvest-lock.d.ts.map +1 -0
  110. package/dist/utils/harvest-lock.js +26 -0
  111. package/dist/utils/harvest-lock.js.map +1 -0
  112. package/dist/utils/harvest-receipts.d.ts +14 -0
  113. package/dist/utils/harvest-receipts.d.ts.map +1 -0
  114. package/dist/utils/harvest-receipts.js +110 -0
  115. package/dist/utils/harvest-receipts.js.map +1 -0
  116. package/dist/utils/listing-cursor.d.ts +31 -0
  117. package/dist/utils/listing-cursor.d.ts.map +1 -0
  118. package/dist/utils/listing-cursor.js +39 -0
  119. package/dist/utils/listing-cursor.js.map +1 -0
  120. package/dist/utils/pot-preflight.d.ts +53 -0
  121. package/dist/utils/pot-preflight.d.ts.map +1 -0
  122. package/dist/utils/pot-preflight.js +146 -0
  123. package/dist/utils/pot-preflight.js.map +1 -0
  124. package/dist/utils/sleep.d.ts +9 -0
  125. package/dist/utils/sleep.d.ts.map +1 -0
  126. package/dist/utils/sleep.js +25 -0
  127. package/dist/utils/sleep.js.map +1 -0
  128. package/dist/utils/store-health.d.ts +37 -0
  129. package/dist/utils/store-health.d.ts.map +1 -0
  130. package/dist/utils/store-health.js +92 -0
  131. package/dist/utils/store-health.js.map +1 -0
  132. package/dist/utils/store-migrations.d.ts +33 -0
  133. package/dist/utils/store-migrations.d.ts.map +1 -0
  134. package/dist/utils/store-migrations.js +41 -0
  135. package/dist/utils/store-migrations.js.map +1 -0
  136. package/dist/utils/store-paths.d.ts +33 -0
  137. package/dist/utils/store-paths.d.ts.map +1 -0
  138. package/dist/utils/store-paths.js +48 -0
  139. package/dist/utils/store-paths.js.map +1 -0
  140. package/dist/utils/store-rows.d.ts +22 -0
  141. package/dist/utils/store-rows.d.ts.map +1 -0
  142. package/dist/utils/store-rows.js +62 -0
  143. package/dist/utils/store-rows.js.map +1 -0
  144. package/dist/utils/store-schema.d.ts +16 -0
  145. package/dist/utils/store-schema.d.ts.map +1 -0
  146. package/dist/utils/store-schema.js +168 -0
  147. package/dist/utils/store-schema.js.map +1 -0
  148. package/dist/utils/store.d.ts +34 -0
  149. package/dist/utils/store.d.ts.map +1 -0
  150. package/dist/utils/store.js +157 -0
  151. package/dist/utils/store.js.map +1 -0
  152. package/dist/utils/video-stats-cache.d.ts +16 -0
  153. package/dist/utils/video-stats-cache.d.ts.map +1 -0
  154. package/dist/utils/video-stats-cache.js +81 -0
  155. package/dist/utils/video-stats-cache.js.map +1 -0
  156. package/dist/utils/video-stats.d.ts +186 -0
  157. package/dist/utils/video-stats.d.ts.map +1 -0
  158. package/dist/utils/video-stats.js +168 -0
  159. package/dist/utils/video-stats.js.map +1 -0
  160. package/dist/utils/video-store.d.ts +69 -0
  161. package/dist/utils/video-store.d.ts.map +1 -0
  162. package/dist/utils/video-store.js +77 -0
  163. package/dist/utils/video-store.js.map +1 -0
  164. package/dist/utils/youtube-channel.d.ts +9 -2
  165. package/dist/utils/youtube-channel.d.ts.map +1 -1
  166. package/dist/utils/youtube-channel.js +33 -3
  167. package/dist/utils/youtube-channel.js.map +1 -1
  168. package/dist/utils/youtube-schemas.d.ts +98 -0
  169. package/dist/utils/youtube-schemas.d.ts.map +1 -1
  170. package/dist/utils/youtube-schemas.js +30 -0
  171. package/dist/utils/youtube-schemas.js.map +1 -1
  172. package/dist/utils/youtube-video.d.ts +22 -1
  173. package/dist/utils/youtube-video.d.ts.map +1 -1
  174. package/dist/utils/youtube-video.js +40 -28
  175. package/dist/utils/youtube-video.js.map +1 -1
  176. package/dist/utils/ytdlp-limiter.d.ts +3 -0
  177. package/dist/utils/ytdlp-limiter.d.ts.map +1 -1
  178. package/dist/utils/ytdlp-limiter.js +19 -3
  179. package/dist/utils/ytdlp-limiter.js.map +1 -1
  180. package/dist/utils/ytdlp-pacer.d.ts +64 -0
  181. package/dist/utils/ytdlp-pacer.d.ts.map +1 -0
  182. package/dist/utils/ytdlp-pacer.js +165 -0
  183. package/dist/utils/ytdlp-pacer.js.map +1 -0
  184. package/dist/utils/ytdlp-stream.d.ts +32 -0
  185. package/dist/utils/ytdlp-stream.d.ts.map +1 -0
  186. package/dist/utils/ytdlp-stream.js +61 -0
  187. package/dist/utils/ytdlp-stream.js.map +1 -0
  188. package/dist/utils/ytdlp.d.ts +10 -0
  189. package/dist/utils/ytdlp.d.ts.map +1 -1
  190. package/dist/utils/ytdlp.js +36 -25
  191. package/dist/utils/ytdlp.js.map +1 -1
  192. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"youtube-channel.d.ts","sourceRoot":"","sources":["../../src/utils/youtube-channel.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAK3B,wDAAwD;AAExD,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,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAkB3F;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
+ {"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
- export async function listVideos(urlOrChannel, limit = 20) {
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
- '--playlist-end',
14
- limit.toString(),
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":"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"}
1
+ {"version":3,"file":"youtube-channel.js","sourceRoot":"","sources":["../../src/utils/youtube-channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,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;AAEpE,wDAAwD;AAExD,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AA2C/E,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,YAAoB,EACpB,KAAK,GAAG,EAAE,EACV,KAAK,GAAG,CAAC;IAET,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B;QACE,iBAAiB;QACjB,iBAAiB;QACjB,iBAAiB;QACjB,SAAS;QACT,mBAAmB;QACnB,sEAAsE;QACtE,sEAAsE;QACtE,wEAAwE;QACxE,kBAAkB;QAClB,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC,EAAE;KAChD,EACD;QACE,KAAK,EAAE,cAAc;QACrB,SAAS,EAAE,QAAQ,CAAC,UAAU;QAC9B,MAAM,EAAE,iBAAiB,CAAC,YAAY,CAAC;KACxC,CACF,CAAC;IAEF,OAAO,mBAAmB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;AAC3E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,YAAoB;IACtD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,CAAC,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,GAAG,CAAC,EAClE;YACE,KAAK,EAAE,oBAAoB;YAC3B,SAAS,EAAE,QAAQ,CAAC,QAAQ;YAC5B,MAAM,EAAE,iBAAiB,CAAC,YAAY,CAAC;SACxC,CACF,CAAC;QACF,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,mBAAmB,EAAE,kBAAkB,CAAC,CAAC;QAC/E,OAAO,MAAM,CAAC,cAAc,IAAI,SAAS,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;QACtE,sEAAsE;QACtE,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,WAAmB;IACvD,MAAM,MAAM,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,CAAC,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,GAAG,CAAC,EAClE;QACE,KAAK,EAAE,mBAAmB;QAC1B,MAAM;KACP,CACF,CAAC;IAEF,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;IAC7E,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;IAEzC,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;QACjB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS;QAC9B,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE;QAC3B,MAAM,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;QAC9B,UAAU,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;QAClC,UAAU,EAAE,IAAI,CAAC,cAAc,IAAI,CAAC;QACpC,YAAY,EAAE,iBAAiB,CAAC,OAAO,CAAC;QACxC,GAAG,EAAE,IAAI,CAAC,WAAW,IAAI,WAAW;QACpC,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;KACpC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAe;IAClD,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAE1C,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,CAAC,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,GAAG,CAAC,EAClE;QACE,KAAK,EAAE,kBAAkB;QACzB,MAAM,EAAE,UAAU;KACnB,CACF,CAAC;IAEF,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,EAAE,iBAAiB,EAAE,kBAAkB,CAAC,CAAC;IAE3E,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,OAAO,IAAI,SAAS;QAC/B,SAAS,EAAE,IAAI,CAAC,UAAU,IAAI,EAAE;QAChC,MAAM,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;QAC9B,eAAe,EAAE,IAAI,CAAC,sBAAsB,IAAI,CAAC;QACjD,UAAU,EAAE,IAAI,CAAC,WAAW,IAAI,UAAU;QAC1C,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;QACnC,2EAA2E;QAC3E,oBAAoB;QACpB,GAAG,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC;KACxC,CAAC;AACJ,CAAC"}
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIhC,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;EAM3B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAE5B,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"}
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":"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"}
1
+ {"version":3,"file":"youtube-schemas.js","sourceRoot":"","sources":["../../src/utils/youtube-schemas.ts"],"names":[],"mappings":"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"}
@@ -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 declare function getComments(urlOrId: string, limit?: number): Promise<VideoComment[]>;
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":"AAMA,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;CACrB;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAqB5E;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA2BtF"}
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, videoDetailsRowSchema, videoInfoRowSchema } from './youtube-schemas.js';
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
- const videoId = extractVideoId(urlOrId);
58
- const url = watchUrl(videoId);
59
- const stdout = await runYtDlp(['-j', '--skip-download'], {
60
- label: 'get_video_details',
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: formatYouTubeDate(data.upload_date ?? ''),
66
- durationSeconds: typeof data.duration === 'number' ? data.duration : 0,
67
- chapters: (data.chapters ?? []).map((ch) => ({
68
- title: ch.title,
69
- startTime: ch.start_time,
70
- startTimeFormatted: formatDuration(Math.floor(ch.start_time)),
71
- endTime: ch.end_time,
72
- endTimeFormatted: formatDuration(Math.floor(ch.end_time)),
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, limit = 20) {
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
- `youtube:comment_sort=top;max_comments=${limit}`,
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 comments = data.comments ?? [];
88
- return comments
89
- .filter((c) => c.parent === 'root')
90
- .slice(0, limit)
91
- .map((c) => ({
92
- author: c.author ?? 'Unknown',
93
- text: c.text ?? '',
94
- likeCount: c.like_count ?? 0,
95
- isPinned: c.is_pinned ?? false,
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":"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"}
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,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,SAAS,EAAyB,MAAM,sBAAsB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAmCxD,MAAM,mBAAmB,GACvB,sIAAsI,CAAC;AAEzI,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAe;IAChD,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAE9B,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B;QACE,iBAAiB;QACjB,2EAA2E;QAC3E,wEAAwE;QACxE,mEAAmE;QACnE,uEAAuE;QACvE,kDAAkD;QAClD,2BAA2B;QAC3B,0EAA0E;QAC1E,yEAAyE;QACzE,wEAAwE;QACxE,wBAAwB;QACxB,SAAS;QACT,mBAAmB;KACpB,EACD,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE,CACzC,CAAC;IAEF,MAAM,GAAG,GAAG,cAAc,CAAC,MAAM,EAAE,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;IAEzE,6EAA6E;IAC7E,4CAA4C;IAC5C,MAAM,OAAO,GAAG,mBAAmB,CAAC;QAClC,YAAY,EAAE,GAAG,CAAC,YAAY,IAAI,SAAS;QAC3C,UAAU,EAAE,GAAG,CAAC,WAAW,IAAI,SAAS;KACzC,CAAC,CAAC;IACH,IAAI,OAAO;QAAE,MAAM,OAAO,CAAC;IAE3B,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC;IAEnC,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,OAAO;QACrB,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,EAAE;QACtB,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE;QAC1B,QAAQ;QACR,iBAAiB,EAAE,cAAc,CAAC,QAAQ,CAAC;QAC3C,UAAU,EAAE,iBAAiB,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;QACpD,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE;QAClC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;YAC3B,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAiB,EAAE,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC;YAClE,CAAC,CAAC,EAAE;QACN,GAAG;QACH,YAAY,EAAE,GAAG,CAAC,SAAS,IAAI,EAAE;QACjC,SAAS,EAAE,GAAG,CAAC,UAAU,IAAI,CAAC;QAC9B,SAAS,EAAE,GAAG,CAAC,UAAU,IAAI,CAAC;QAC9B,YAAY,EAAE,GAAG,CAAC,aAAa,IAAI,CAAC;KACrC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe;IAC/C,OAAO,CAAC,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnD,CAAC;AAkBD,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAe;IACnD,0EAA0E;IAC1E,4EAA4E;IAC5E,yEAAyE;IACzE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC;IAEhD,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACzC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,kBAAkB,EAAE,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACjE,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,gBAAgB,EAAE,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;SAC9D,CAAC,CAAC;QACH,GAAG,CAAC,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC;KAClF,CAAC;AACJ,CAAC;AAiBD,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAAe,EACf,UAAkF,EAAE;IAEpF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,KAAK,CAAC;IACnC,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAE9B,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B;QACE,IAAI;QACJ,iBAAiB;QACjB,kBAAkB;QAClB,kBAAkB;QAClB,mEAAmE;QACnE,wEAAwE;QACxE,uEAAuE;QACvE,oDAAoD;QACpD,kBAAkB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,IAAI,GAAG,EAAE,CAAC;KAC7F,EACD,EAAE,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CACrE,CAAC;IAEF,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,EAAE,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;IACzE,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IAEjC,OAAO;QACL,GAAG,SAAS,CAAC,IAAI,CAAC;QAClB,eAAe,EAAE,IAAI,CAAC,aAAa,KAAK,IAAI,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS;QAChF,gBAAgB,EACd,CAAC,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC;YACvD,CAAC,IAAI,CAAC,aAAa,KAAK,IAAI,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,CAAC;QACnE,cAAc,EAAE,IAAI,CAAC,MAAM;KAC5B,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,OAI3B;IACC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC;IACrD,OAAO;QACL,wBAAwB,OAAO,CAAC,IAAI,EAAE;QACtC,gBAAgB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,IAAI;KACvH,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC"}
@@ -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":"AAqBA;;;;;;;;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;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAEpF"}
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
- /** A limit of 0 (or NaN) would queue every call forever, so the floor is 1. */
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 < MAX_CONCURRENT) {
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 { active, queued: waiting.length, limit: MAX_CONCURRENT };
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;AAE3C;;;;;GAKG;AAEH,+EAA+E;AAC/E,MAAM,cAAc,GAAG,MAAM,CAAC,6BAA6B,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;AAOrF,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,cAAc,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;IAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC;AACnE,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"}