youtube-knowledge-mcp 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (478) hide show
  1. package/README.md +153 -38
  2. package/dist/brain-schemas.d.ts +10 -10
  3. package/dist/brain-schemas.d.ts.map +1 -1
  4. package/dist/brain-schemas.js +0 -4
  5. package/dist/brain-schemas.js.map +1 -1
  6. package/dist/harvest-schemas.d.ts +75 -0
  7. package/dist/harvest-schemas.d.ts.map +1 -0
  8. package/dist/harvest-schemas.js +106 -0
  9. package/dist/harvest-schemas.js.map +1 -0
  10. package/dist/http/app.d.ts +16 -0
  11. package/dist/http/app.d.ts.map +1 -0
  12. package/dist/http/app.js +176 -0
  13. package/dist/http/app.js.map +1 -0
  14. package/dist/http/auth.d.ts +38 -0
  15. package/dist/http/auth.d.ts.map +1 -0
  16. package/dist/http/auth.js +79 -0
  17. package/dist/http/auth.js.map +1 -0
  18. package/dist/http/config.d.ts +41 -0
  19. package/dist/http/config.d.ts.map +1 -0
  20. package/dist/http/config.js +65 -0
  21. package/dist/http/config.js.map +1 -0
  22. package/dist/http/rate-limiter.d.ts +24 -0
  23. package/dist/http/rate-limiter.d.ts.map +1 -0
  24. package/dist/http/rate-limiter.js +55 -0
  25. package/dist/http/rate-limiter.js.map +1 -0
  26. package/dist/http/sessions.d.ts +34 -0
  27. package/dist/http/sessions.d.ts.map +1 -0
  28. package/dist/http/sessions.js +62 -0
  29. package/dist/http/sessions.js.map +1 -0
  30. package/dist/http.d.ts +6 -48
  31. package/dist/http.d.ts.map +1 -1
  32. package/dist/http.js +35 -252
  33. package/dist/http.js.map +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +11 -517
  36. package/dist/index.js.map +1 -1
  37. package/dist/prompts-brain.d.ts +4 -0
  38. package/dist/prompts-brain.d.ts.map +1 -0
  39. package/dist/prompts-brain.js +48 -0
  40. package/dist/prompts-brain.js.map +1 -0
  41. package/dist/prompts-research.d.ts +4 -0
  42. package/dist/prompts-research.d.ts.map +1 -0
  43. package/dist/prompts-research.js +74 -0
  44. package/dist/prompts-research.js.map +1 -0
  45. package/dist/prompts-shared.d.ts +13 -0
  46. package/dist/prompts-shared.d.ts.map +1 -0
  47. package/dist/prompts-shared.js +7 -0
  48. package/dist/prompts-shared.js.map +1 -0
  49. package/dist/prompts.d.ts.map +1 -1
  50. package/dist/prompts.js +5 -100
  51. package/dist/prompts.js.map +1 -1
  52. package/dist/registry/brain-tools.d.ts +4 -0
  53. package/dist/registry/brain-tools.d.ts.map +1 -0
  54. package/dist/registry/brain-tools.js +104 -0
  55. package/dist/registry/brain-tools.js.map +1 -0
  56. package/dist/registry/channel-tools.d.ts +4 -0
  57. package/dist/registry/channel-tools.d.ts.map +1 -0
  58. package/dist/registry/channel-tools.js +53 -0
  59. package/dist/registry/channel-tools.js.map +1 -0
  60. package/dist/registry/harvest-tools.d.ts +10 -0
  61. package/dist/registry/harvest-tools.d.ts.map +1 -0
  62. package/dist/registry/harvest-tools.js +109 -0
  63. package/dist/registry/harvest-tools.js.map +1 -0
  64. package/dist/registry/health-tools.d.ts +4 -0
  65. package/dist/registry/health-tools.d.ts.map +1 -0
  66. package/dist/registry/health-tools.js +39 -0
  67. package/dist/registry/health-tools.js.map +1 -0
  68. package/dist/registry/index.d.ts +8 -0
  69. package/dist/registry/index.d.ts.map +1 -0
  70. package/dist/registry/index.js +29 -0
  71. package/dist/registry/index.js.map +1 -0
  72. package/dist/registry/library-tools.d.ts +4 -0
  73. package/dist/registry/library-tools.d.ts.map +1 -0
  74. package/dist/registry/library-tools.js +116 -0
  75. package/dist/registry/library-tools.js.map +1 -0
  76. package/dist/registry/media-tools.d.ts +4 -0
  77. package/dist/registry/media-tools.d.ts.map +1 -0
  78. package/dist/registry/media-tools.js +100 -0
  79. package/dist/registry/media-tools.js.map +1 -0
  80. package/dist/registry/thumbnail-tools.d.ts +7 -0
  81. package/dist/registry/thumbnail-tools.d.ts.map +1 -0
  82. package/dist/registry/thumbnail-tools.js +72 -0
  83. package/dist/registry/thumbnail-tools.js.map +1 -0
  84. package/dist/registry/transcript-tools.d.ts +4 -0
  85. package/dist/registry/transcript-tools.d.ts.map +1 -0
  86. package/dist/registry/transcript-tools.js +68 -0
  87. package/dist/registry/transcript-tools.js.map +1 -0
  88. package/dist/registry/types.d.ts +41 -0
  89. package/dist/registry/types.d.ts.map +1 -0
  90. package/dist/registry/types.js +17 -0
  91. package/dist/registry/types.js.map +1 -0
  92. package/dist/registry/video-tools.d.ts +4 -0
  93. package/dist/registry/video-tools.d.ts.map +1 -0
  94. package/dist/registry/video-tools.js +101 -0
  95. package/dist/registry/video-tools.js.map +1 -0
  96. package/dist/resources.d.ts.map +1 -1
  97. package/dist/resources.js +13 -10
  98. package/dist/resources.js.map +1 -1
  99. package/dist/schemas.d.ts +250 -2
  100. package/dist/schemas.d.ts.map +1 -1
  101. package/dist/schemas.js +61 -0
  102. package/dist/schemas.js.map +1 -1
  103. package/dist/thumbnail-resource.d.ts +8 -0
  104. package/dist/thumbnail-resource.d.ts.map +1 -0
  105. package/dist/thumbnail-resource.js +38 -0
  106. package/dist/thumbnail-resource.js.map +1 -0
  107. package/dist/thumbnail-schemas.d.ts +512 -0
  108. package/dist/thumbnail-schemas.d.ts.map +1 -0
  109. package/dist/thumbnail-schemas.js +95 -0
  110. package/dist/thumbnail-schemas.js.map +1 -0
  111. package/dist/tools/ask-brain.js +1 -1
  112. package/dist/tools/ask-brain.js.map +1 -1
  113. package/dist/tools/batch.js +1 -1
  114. package/dist/tools/batch.js.map +1 -1
  115. package/dist/tools/brain-info.js +1 -1
  116. package/dist/tools/brain-info.js.map +1 -1
  117. package/dist/tools/build-brain.js +1 -1
  118. package/dist/tools/build-brain.js.map +1 -1
  119. package/dist/tools/check-health.d.ts +61 -0
  120. package/dist/tools/check-health.d.ts.map +1 -1
  121. package/dist/tools/check-health.js +94 -6
  122. package/dist/tools/check-health.js.map +1 -1
  123. package/dist/tools/clips.d.ts +9 -41
  124. package/dist/tools/clips.d.ts.map +1 -1
  125. package/dist/tools/clips.js +52 -128
  126. package/dist/tools/clips.js.map +1 -1
  127. package/dist/tools/fetch-channel-thumbnails.d.ts +179 -0
  128. package/dist/tools/fetch-channel-thumbnails.d.ts.map +1 -0
  129. package/dist/tools/fetch-channel-thumbnails.js +123 -0
  130. package/dist/tools/fetch-channel-thumbnails.js.map +1 -0
  131. package/dist/tools/fetch-videos.d.ts +18 -4
  132. package/dist/tools/fetch-videos.d.ts.map +1 -1
  133. package/dist/tools/fetch-videos.js +64 -20
  134. package/dist/tools/fetch-videos.js.map +1 -1
  135. package/dist/tools/frames-subtitles.d.ts +44 -0
  136. package/dist/tools/frames-subtitles.d.ts.map +1 -0
  137. package/dist/tools/frames-subtitles.js +104 -0
  138. package/dist/tools/frames-subtitles.js.map +1 -0
  139. package/dist/tools/get-channel-info.d.ts +2 -0
  140. package/dist/tools/get-channel-info.d.ts.map +1 -1
  141. package/dist/tools/get-channel-info.js +8 -1
  142. package/dist/tools/get-channel-info.js.map +1 -1
  143. package/dist/tools/get-comments.d.ts +238 -6
  144. package/dist/tools/get-comments.d.ts.map +1 -1
  145. package/dist/tools/get-comments.js +85 -21
  146. package/dist/tools/get-comments.js.map +1 -1
  147. package/dist/tools/get-coverage.d.ts +122 -0
  148. package/dist/tools/get-coverage.d.ts.map +1 -0
  149. package/dist/tools/get-coverage.js +105 -0
  150. package/dist/tools/get-coverage.js.map +1 -0
  151. package/dist/tools/get-playlist-info.d.ts.map +1 -1
  152. package/dist/tools/get-playlist-info.js +1 -0
  153. package/dist/tools/get-playlist-info.js.map +1 -1
  154. package/dist/tools/get-thumbnail.d.ts +38 -0
  155. package/dist/tools/get-thumbnail.d.ts.map +1 -0
  156. package/dist/tools/get-thumbnail.js +152 -0
  157. package/dist/tools/get-thumbnail.js.map +1 -0
  158. package/dist/tools/get-transcript.d.ts.map +1 -1
  159. package/dist/tools/get-transcript.js +2 -14
  160. package/dist/tools/get-transcript.js.map +1 -1
  161. package/dist/tools/harvest-channel.d.ts +92 -0
  162. package/dist/tools/harvest-channel.d.ts.map +1 -0
  163. package/dist/tools/harvest-channel.js +62 -0
  164. package/dist/tools/harvest-channel.js.map +1 -0
  165. package/dist/tools/harvest-comments.d.ts +81 -0
  166. package/dist/tools/harvest-comments.d.ts.map +1 -0
  167. package/dist/tools/harvest-comments.js +115 -0
  168. package/dist/tools/harvest-comments.js.map +1 -0
  169. package/dist/tools/library.d.ts.map +1 -1
  170. package/dist/tools/library.js +14 -4
  171. package/dist/tools/library.js.map +1 -1
  172. package/dist/tools/list-channel-thumbnails.d.ts +235 -0
  173. package/dist/tools/list-channel-thumbnails.d.ts.map +1 -0
  174. package/dist/tools/list-channel-thumbnails.js +79 -0
  175. package/dist/tools/list-channel-thumbnails.js.map +1 -0
  176. package/dist/tools/list-library.js +1 -1
  177. package/dist/tools/list-library.js.map +1 -1
  178. package/dist/tools/prune-harvest.d.ts +59 -0
  179. package/dist/tools/prune-harvest.d.ts.map +1 -0
  180. package/dist/tools/prune-harvest.js +108 -0
  181. package/dist/tools/prune-harvest.js.map +1 -0
  182. package/dist/tools/query-comments.d.ts +125 -0
  183. package/dist/tools/query-comments.d.ts.map +1 -0
  184. package/dist/tools/query-comments.js +106 -0
  185. package/dist/tools/query-comments.js.map +1 -0
  186. package/dist/tools/query-videos.d.ts +118 -0
  187. package/dist/tools/query-videos.d.ts.map +1 -0
  188. package/dist/tools/query-videos.js +97 -0
  189. package/dist/tools/query-videos.js.map +1 -0
  190. package/dist/tools/repair-store.d.ts +34 -0
  191. package/dist/tools/repair-store.d.ts.map +1 -0
  192. package/dist/tools/repair-store.js +58 -0
  193. package/dist/tools/repair-store.js.map +1 -0
  194. package/dist/tools/save-to-library.d.ts.map +1 -1
  195. package/dist/tools/save-to-library.js +5 -1
  196. package/dist/tools/save-to-library.js.map +1 -1
  197. package/dist/tools/search-channels.d.ts +6 -0
  198. package/dist/tools/search-channels.d.ts.map +1 -1
  199. package/dist/tools/search-channels.js +5 -1
  200. package/dist/tools/search-channels.js.map +1 -1
  201. package/dist/tools/search-transcript.d.ts.map +1 -1
  202. package/dist/tools/search-transcript.js +6 -13
  203. package/dist/tools/search-transcript.js.map +1 -1
  204. package/dist/tools/search-videos.d.ts +3 -0
  205. package/dist/tools/search-videos.d.ts.map +1 -1
  206. package/dist/tools/search-videos.js +1 -1
  207. package/dist/tools/search-videos.js.map +1 -1
  208. package/dist/utils/batches.d.ts +15 -0
  209. package/dist/utils/batches.d.ts.map +1 -0
  210. package/dist/utils/batches.js +31 -0
  211. package/dist/utils/batches.js.map +1 -0
  212. package/dist/utils/brain-build.d.ts.map +1 -1
  213. package/dist/utils/brain-build.js +1 -12
  214. package/dist/utils/brain-build.js.map +1 -1
  215. package/dist/utils/brain-index.d.ts.map +1 -1
  216. package/dist/utils/brain-index.js +3 -2
  217. package/dist/utils/brain-index.js.map +1 -1
  218. package/dist/utils/brain-lock.d.ts +2 -18
  219. package/dist/utils/brain-lock.d.ts.map +1 -1
  220. package/dist/utils/brain-lock.js +4 -80
  221. package/dist/utils/brain-lock.js.map +1 -1
  222. package/dist/utils/brain-lookup.d.ts +8 -0
  223. package/dist/utils/brain-lookup.d.ts.map +1 -1
  224. package/dist/utils/brain-lookup.js +5 -31
  225. package/dist/utils/brain-lookup.js.map +1 -1
  226. package/dist/utils/brain-storage.d.ts.map +1 -1
  227. package/dist/utils/brain-storage.js +5 -5
  228. package/dist/utils/brain-storage.js.map +1 -1
  229. package/dist/utils/caption-probe.d.ts +7 -0
  230. package/dist/utils/caption-probe.d.ts.map +1 -0
  231. package/dist/utils/caption-probe.js +36 -0
  232. package/dist/utils/caption-probe.js.map +1 -0
  233. package/dist/utils/channel-images.d.ts +16 -0
  234. package/dist/utils/channel-images.d.ts.map +1 -0
  235. package/dist/utils/channel-images.js +35 -0
  236. package/dist/utils/channel-images.js.map +1 -0
  237. package/dist/utils/channel-lookup.d.ts +5 -0
  238. package/dist/utils/channel-lookup.d.ts.map +1 -0
  239. package/dist/utils/channel-lookup.js +37 -0
  240. package/dist/utils/channel-lookup.js.map +1 -0
  241. package/dist/utils/chapters.d.ts +11 -0
  242. package/dist/utils/chapters.d.ts.map +1 -0
  243. package/dist/utils/chapters.js +24 -0
  244. package/dist/utils/chapters.js.map +1 -0
  245. package/dist/utils/clips.d.ts.map +1 -1
  246. package/dist/utils/clips.js +18 -26
  247. package/dist/utils/clips.js.map +1 -1
  248. package/dist/utils/comment-store.d.ts +71 -0
  249. package/dist/utils/comment-store.d.ts.map +1 -0
  250. package/dist/utils/comment-store.js +129 -0
  251. package/dist/utils/comment-store.js.map +1 -0
  252. package/dist/utils/comment-threads.d.ts +51 -0
  253. package/dist/utils/comment-threads.d.ts.map +1 -0
  254. package/dist/utils/comment-threads.js +76 -0
  255. package/dist/utils/comment-threads.js.map +1 -0
  256. package/dist/utils/coverage-text.d.ts +3 -0
  257. package/dist/utils/coverage-text.d.ts.map +1 -0
  258. package/dist/utils/coverage-text.js +62 -0
  259. package/dist/utils/coverage-text.js.map +1 -0
  260. package/dist/utils/coverage.d.ts +57 -0
  261. package/dist/utils/coverage.d.ts.map +1 -0
  262. package/dist/utils/coverage.js +142 -0
  263. package/dist/utils/coverage.js.map +1 -0
  264. package/dist/utils/env.d.ts +26 -0
  265. package/dist/utils/env.d.ts.map +1 -0
  266. package/dist/utils/env.js +72 -0
  267. package/dist/utils/env.js.map +1 -0
  268. package/dist/utils/errors.d.ts +1 -1
  269. package/dist/utils/errors.d.ts.map +1 -1
  270. package/dist/utils/errors.js +28 -2
  271. package/dist/utils/errors.js.map +1 -1
  272. package/dist/utils/file-lock.d.ts +34 -0
  273. package/dist/utils/file-lock.d.ts.map +1 -0
  274. package/dist/utils/file-lock.js +91 -0
  275. package/dist/utils/file-lock.js.map +1 -0
  276. package/dist/utils/flat-listing.d.ts +89 -0
  277. package/dist/utils/flat-listing.d.ts.map +1 -0
  278. package/dist/utils/flat-listing.js +68 -0
  279. package/dist/utils/flat-listing.js.map +1 -0
  280. package/dist/utils/format.d.ts +18 -4
  281. package/dist/utils/format.d.ts.map +1 -1
  282. package/dist/utils/format.js +2 -6
  283. package/dist/utils/format.js.map +1 -1
  284. package/dist/utils/guard.d.ts +21 -0
  285. package/dist/utils/guard.d.ts.map +1 -0
  286. package/dist/utils/guard.js +105 -0
  287. package/dist/utils/guard.js.map +1 -0
  288. package/dist/utils/harvest-catalog.d.ts +44 -0
  289. package/dist/utils/harvest-catalog.d.ts.map +1 -0
  290. package/dist/utils/harvest-catalog.js +164 -0
  291. package/dist/utils/harvest-catalog.js.map +1 -0
  292. package/dist/utils/harvest-lock.d.ts +3 -0
  293. package/dist/utils/harvest-lock.d.ts.map +1 -0
  294. package/dist/utils/harvest-lock.js +26 -0
  295. package/dist/utils/harvest-lock.js.map +1 -0
  296. package/dist/utils/harvest-receipts.d.ts +14 -0
  297. package/dist/utils/harvest-receipts.d.ts.map +1 -0
  298. package/dist/utils/harvest-receipts.js +110 -0
  299. package/dist/utils/harvest-receipts.js.map +1 -0
  300. package/dist/utils/image-dimensions.d.ts +19 -0
  301. package/dist/utils/image-dimensions.d.ts.map +1 -0
  302. package/dist/utils/image-dimensions.js +93 -0
  303. package/dist/utils/image-dimensions.js.map +1 -0
  304. package/dist/utils/image-fetch.d.ts +31 -0
  305. package/dist/utils/image-fetch.d.ts.map +1 -0
  306. package/dist/utils/image-fetch.js +141 -0
  307. package/dist/utils/image-fetch.js.map +1 -0
  308. package/dist/utils/json-file.d.ts +14 -5
  309. package/dist/utils/json-file.d.ts.map +1 -1
  310. package/dist/utils/json-file.js +24 -9
  311. package/dist/utils/json-file.js.map +1 -1
  312. package/dist/utils/listing-cursor.d.ts +31 -0
  313. package/dist/utils/listing-cursor.d.ts.map +1 -0
  314. package/dist/utils/listing-cursor.js +39 -0
  315. package/dist/utils/listing-cursor.js.map +1 -0
  316. package/dist/utils/paths.d.ts +10 -0
  317. package/dist/utils/paths.d.ts.map +1 -1
  318. package/dist/utils/paths.js +14 -0
  319. package/dist/utils/paths.js.map +1 -1
  320. package/dist/utils/pattern.d.ts +30 -0
  321. package/dist/utils/pattern.d.ts.map +1 -0
  322. package/dist/utils/pattern.js +130 -0
  323. package/dist/utils/pattern.js.map +1 -0
  324. package/dist/utils/pot-preflight.d.ts +53 -0
  325. package/dist/utils/pot-preflight.d.ts.map +1 -0
  326. package/dist/utils/pot-preflight.js +146 -0
  327. package/dist/utils/pot-preflight.js.map +1 -0
  328. package/dist/utils/search-index.d.ts.map +1 -1
  329. package/dist/utils/search-index.js +9 -4
  330. package/dist/utils/search-index.js.map +1 -1
  331. package/dist/utils/sleep.d.ts +9 -0
  332. package/dist/utils/sleep.d.ts.map +1 -0
  333. package/dist/utils/sleep.js +25 -0
  334. package/dist/utils/sleep.js.map +1 -0
  335. package/dist/utils/storage.d.ts.map +1 -1
  336. package/dist/utils/storage.js +14 -8
  337. package/dist/utils/storage.js.map +1 -1
  338. package/dist/utils/store-health.d.ts +37 -0
  339. package/dist/utils/store-health.d.ts.map +1 -0
  340. package/dist/utils/store-health.js +92 -0
  341. package/dist/utils/store-health.js.map +1 -0
  342. package/dist/utils/store-migrations.d.ts +33 -0
  343. package/dist/utils/store-migrations.d.ts.map +1 -0
  344. package/dist/utils/store-migrations.js +41 -0
  345. package/dist/utils/store-migrations.js.map +1 -0
  346. package/dist/utils/store-paths.d.ts +33 -0
  347. package/dist/utils/store-paths.d.ts.map +1 -0
  348. package/dist/utils/store-paths.js +48 -0
  349. package/dist/utils/store-paths.js.map +1 -0
  350. package/dist/utils/store-rows.d.ts +22 -0
  351. package/dist/utils/store-rows.d.ts.map +1 -0
  352. package/dist/utils/store-rows.js +62 -0
  353. package/dist/utils/store-rows.js.map +1 -0
  354. package/dist/utils/store-schema.d.ts +16 -0
  355. package/dist/utils/store-schema.d.ts.map +1 -0
  356. package/dist/utils/store-schema.js +168 -0
  357. package/dist/utils/store-schema.js.map +1 -0
  358. package/dist/utils/store.d.ts +34 -0
  359. package/dist/utils/store.d.ts.map +1 -0
  360. package/dist/utils/store.js +157 -0
  361. package/dist/utils/store.js.map +1 -0
  362. package/dist/utils/subtitles.d.ts +7 -0
  363. package/dist/utils/subtitles.d.ts.map +1 -0
  364. package/dist/utils/subtitles.js +30 -0
  365. package/dist/utils/subtitles.js.map +1 -0
  366. package/dist/utils/thumbnail-entry.d.ts +19 -0
  367. package/dist/utils/thumbnail-entry.d.ts.map +1 -0
  368. package/dist/utils/thumbnail-entry.js +119 -0
  369. package/dist/utils/thumbnail-entry.js.map +1 -0
  370. package/dist/utils/thumbnail-fetch.d.ts +37 -0
  371. package/dist/utils/thumbnail-fetch.d.ts.map +1 -0
  372. package/dist/utils/thumbnail-fetch.js +89 -0
  373. package/dist/utils/thumbnail-fetch.js.map +1 -0
  374. package/dist/utils/thumbnail-ladder.d.ts +44 -0
  375. package/dist/utils/thumbnail-ladder.d.ts.map +1 -0
  376. package/dist/utils/thumbnail-ladder.js +68 -0
  377. package/dist/utils/thumbnail-ladder.js.map +1 -0
  378. package/dist/utils/thumbnail-listing.d.ts +16 -0
  379. package/dist/utils/thumbnail-listing.d.ts.map +1 -0
  380. package/dist/utils/thumbnail-listing.js +26 -0
  381. package/dist/utils/thumbnail-listing.js.map +1 -0
  382. package/dist/utils/thumbnail-paths.d.ts +20 -0
  383. package/dist/utils/thumbnail-paths.d.ts.map +1 -0
  384. package/dist/utils/thumbnail-paths.js +35 -0
  385. package/dist/utils/thumbnail-paths.js.map +1 -0
  386. package/dist/utils/thumbnail-store.d.ts +19 -0
  387. package/dist/utils/thumbnail-store.d.ts.map +1 -0
  388. package/dist/utils/thumbnail-store.js +125 -0
  389. package/dist/utils/thumbnail-store.js.map +1 -0
  390. package/dist/utils/transcript-cache.d.ts +20 -0
  391. package/dist/utils/transcript-cache.d.ts.map +1 -0
  392. package/dist/utils/transcript-cache.js +127 -0
  393. package/dist/utils/transcript-cache.js.map +1 -0
  394. package/dist/utils/transcript-search.d.ts +20 -0
  395. package/dist/utils/transcript-search.d.ts.map +1 -0
  396. package/dist/utils/transcript-search.js +35 -0
  397. package/dist/utils/transcript-search.js.map +1 -0
  398. package/dist/utils/transcript.d.ts +10 -31
  399. package/dist/utils/transcript.d.ts.map +1 -1
  400. package/dist/utils/transcript.js +2 -64
  401. package/dist/utils/transcript.js.map +1 -1
  402. package/dist/utils/validate-youtube.d.ts +35 -0
  403. package/dist/utils/validate-youtube.d.ts.map +1 -0
  404. package/dist/utils/validate-youtube.js +87 -0
  405. package/dist/utils/validate-youtube.js.map +1 -0
  406. package/dist/utils/validate.d.ts +11 -4
  407. package/dist/utils/validate.d.ts.map +1 -1
  408. package/dist/utils/validate.js +43 -36
  409. package/dist/utils/validate.js.map +1 -1
  410. package/dist/utils/version.d.ts +2 -0
  411. package/dist/utils/version.d.ts.map +1 -0
  412. package/dist/utils/version.js +19 -0
  413. package/dist/utils/version.js.map +1 -0
  414. package/dist/utils/video-stats-cache.d.ts +16 -0
  415. package/dist/utils/video-stats-cache.d.ts.map +1 -0
  416. package/dist/utils/video-stats-cache.js +81 -0
  417. package/dist/utils/video-stats-cache.js.map +1 -0
  418. package/dist/utils/video-stats.d.ts +186 -0
  419. package/dist/utils/video-stats.d.ts.map +1 -0
  420. package/dist/utils/video-stats.js +168 -0
  421. package/dist/utils/video-stats.js.map +1 -0
  422. package/dist/utils/video-store.d.ts +69 -0
  423. package/dist/utils/video-store.d.ts.map +1 -0
  424. package/dist/utils/video-store.js +77 -0
  425. package/dist/utils/video-store.js.map +1 -0
  426. package/dist/utils/youtube-channel.d.ts +51 -0
  427. package/dist/utils/youtube-channel.d.ts.map +1 -0
  428. package/dist/utils/youtube-channel.js +91 -0
  429. package/dist/utils/youtube-channel.js.map +1 -0
  430. package/dist/utils/youtube-download.d.ts +27 -0
  431. package/dist/utils/youtube-download.d.ts.map +1 -0
  432. package/dist/utils/youtube-download.js +118 -0
  433. package/dist/utils/youtube-download.js.map +1 -0
  434. package/dist/utils/youtube-schemas.d.ts +492 -0
  435. package/dist/utils/youtube-schemas.d.ts.map +1 -0
  436. package/dist/utils/youtube-schemas.js +148 -0
  437. package/dist/utils/youtube-schemas.js.map +1 -0
  438. package/dist/utils/youtube-search.d.ts +14 -0
  439. package/dist/utils/youtube-search.d.ts.map +1 -0
  440. package/dist/utils/youtube-search.js +39 -0
  441. package/dist/utils/youtube-search.js.map +1 -0
  442. package/dist/utils/youtube-url.d.ts +25 -0
  443. package/dist/utils/youtube-url.d.ts.map +1 -0
  444. package/dist/utils/youtube-url.js +75 -0
  445. package/dist/utils/youtube-url.js.map +1 -0
  446. package/dist/utils/youtube-video.d.ts +68 -0
  447. package/dist/utils/youtube-video.d.ts.map +1 -0
  448. package/dist/utils/youtube-video.js +110 -0
  449. package/dist/utils/youtube-video.js.map +1 -0
  450. package/dist/utils/youtube.d.ts +10 -122
  451. package/dist/utils/youtube.d.ts.map +1 -1
  452. package/dist/utils/youtube.js +11 -486
  453. package/dist/utils/youtube.js.map +1 -1
  454. package/dist/utils/ytdlp-env.d.ts +33 -0
  455. package/dist/utils/ytdlp-env.d.ts.map +1 -0
  456. package/dist/utils/ytdlp-env.js +121 -0
  457. package/dist/utils/ytdlp-env.js.map +1 -0
  458. package/dist/utils/ytdlp-limiter.d.ts +27 -0
  459. package/dist/utils/ytdlp-limiter.d.ts.map +1 -0
  460. package/dist/utils/ytdlp-limiter.js +85 -0
  461. package/dist/utils/ytdlp-limiter.js.map +1 -0
  462. package/dist/utils/ytdlp-pacer.d.ts +64 -0
  463. package/dist/utils/ytdlp-pacer.d.ts.map +1 -0
  464. package/dist/utils/ytdlp-pacer.js +165 -0
  465. package/dist/utils/ytdlp-pacer.js.map +1 -0
  466. package/dist/utils/ytdlp-parse.d.ts +13 -0
  467. package/dist/utils/ytdlp-parse.d.ts.map +1 -0
  468. package/dist/utils/ytdlp-parse.js +48 -0
  469. package/dist/utils/ytdlp-parse.js.map +1 -0
  470. package/dist/utils/ytdlp-stream.d.ts +32 -0
  471. package/dist/utils/ytdlp-stream.d.ts.map +1 -0
  472. package/dist/utils/ytdlp-stream.js +61 -0
  473. package/dist/utils/ytdlp-stream.js.map +1 -0
  474. package/dist/utils/ytdlp.d.ts +23 -18
  475. package/dist/utils/ytdlp.d.ts.map +1 -1
  476. package/dist/utils/ytdlp.js +56 -134
  477. package/dist/utils/ytdlp.js.map +1 -1
  478. package/package.json +3 -2
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Find the records that belong to a channel, from whatever the caller called it.
3
+ *
4
+ * Every local tool answers from disk, so none of them should need the network
5
+ * to work out which channel is meant — asking YouTube to resolve a handle
6
+ * before every search would put a round trip in front of a lookup that is
7
+ * otherwise instant, and would make the data unusable offline. A stored record
8
+ * already carries the channel's id, handle, name and URL, which is everything
9
+ * a caller is likely to type.
10
+ */
11
+ const CHANNEL_ID_IN_TEXT = /UC[A-Za-z0-9_-]{22}/;
12
+ const HANDLE_IN_TEXT = /@[\w.-]+/;
13
+ export function findByChannel(items, channel) {
14
+ const wanted = candidates(channel);
15
+ return items.filter((item) => keysOf(item.channel).some((key) => wanted.has(key)));
16
+ }
17
+ function candidates(channel) {
18
+ const keys = new Set([normalize(channel)]);
19
+ const id = CHANNEL_ID_IN_TEXT.exec(channel)?.[0];
20
+ if (id !== undefined)
21
+ keys.add(normalize(id));
22
+ const handle = HANDLE_IN_TEXT.exec(channel)?.[0];
23
+ if (handle !== undefined) {
24
+ keys.add(normalize(handle));
25
+ keys.add(normalize(handle.slice(1)));
26
+ }
27
+ return keys;
28
+ }
29
+ function keysOf({ channelId, handle, name, channelUrl }) {
30
+ return [channelId, handle, handle.replace(/^@/, ''), name, channelUrl]
31
+ .map(normalize)
32
+ .filter((key) => key !== '');
33
+ }
34
+ function normalize(value) {
35
+ return value.trim().toLowerCase().replace(/\/+$/, '');
36
+ }
37
+ //# sourceMappingURL=channel-lookup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-lookup.js","sourceRoot":"","sources":["../../src/utils/channel-lookup.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,MAAM,kBAAkB,GAAG,qBAAqB,CAAC;AACjD,MAAM,cAAc,GAAG,UAAU,CAAC;AAElC,MAAM,UAAU,aAAa,CAC3B,KAAU,EACV,OAAe;IAEf,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,SAAS,UAAU,CAAC,OAAe;IACjC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAEnD,MAAM,EAAE,GAAG,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjD,IAAI,EAAE,KAAK,SAAS;QAAE,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,MAAM,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAe;IAClE,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC;SACnE,GAAG,CAAC,SAAS,CAAC;SACd,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { Chapter } from './youtube.js';
2
+ /**
3
+ * Resolving a chapter name a caller typed to a chapter the video actually has.
4
+ *
5
+ * Exact title first, then a substring, both case-insensitive: people remember
6
+ * "the middle" of a talk, not "03 — The Middle Section (part 2)". Two tools
7
+ * used to carry identical copies of this; a mismatch in how they matched
8
+ * would have meant a chapter that clips but cannot be read, or the reverse.
9
+ */
10
+ export declare function resolveChapter(chapters: Chapter[], name: string, whenNone: string): Chapter;
11
+ //# sourceMappingURL=chapters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chapters.d.ts","sourceRoot":"","sources":["../../src/utils/chapters.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAiB3F"}
@@ -0,0 +1,24 @@
1
+ import { YouTubeError } from './errors.js';
2
+ /**
3
+ * Resolving a chapter name a caller typed to a chapter the video actually has.
4
+ *
5
+ * Exact title first, then a substring, both case-insensitive: people remember
6
+ * "the middle" of a talk, not "03 — The Middle Section (part 2)". Two tools
7
+ * used to carry identical copies of this; a mismatch in how they matched
8
+ * would have meant a chapter that clips but cannot be read, or the reverse.
9
+ */
10
+ export function resolveChapter(chapters, name, whenNone) {
11
+ if (chapters.length === 0) {
12
+ throw new YouTubeError('NOT_FOUND', 'This video has no chapters.', { nextStep: whenNone });
13
+ }
14
+ const wanted = name.toLowerCase();
15
+ const match = chapters.find((chapter) => chapter.title.toLowerCase() === wanted) ??
16
+ chapters.find((chapter) => chapter.title.toLowerCase().includes(wanted));
17
+ if (!match) {
18
+ throw new YouTubeError('NOT_FOUND', `No chapter matches "${name}".`, {
19
+ nextStep: `Available chapters: ${chapters.map((chapter) => chapter.title).join(', ')}`,
20
+ });
21
+ }
22
+ return match;
23
+ }
24
+ //# sourceMappingURL=chapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chapters.js","sourceRoot":"","sources":["../../src/utils/chapters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,QAAmB,EAAE,IAAY,EAAE,QAAgB;IAChF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,6BAA6B,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAClC,MAAM,KAAK,GACT,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;QAClE,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAE3E,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,uBAAuB,IAAI,IAAI,EAAE;YACnE,QAAQ,EAAE,uBAAuB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SACvF,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"clips.d.ts","sourceRoot":"","sources":["../../src/utils/clips.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgBhE;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D,OAAO,CAAC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAoDjF;AAMD,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAChE,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,UAAU,CAAC,CA+DrB;AAMD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,KAAK,GAAG,KAAK,CAAA;CAAE,GACrD,OAAO,CAAC,WAAW,CAAC,CA8DtB;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
1
+ {"version":3,"file":"clips.d.ts","sourceRoot":"","sources":["../../src/utils/clips.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAgBhE;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1D,OAAO,CAAC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAwCjF;AAMD,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAChE,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,UAAU,CAAC,CA2DrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,KAAK,GAAG,KAAK,CAAA;CAAE,GACrD,OAAO,CAAC,WAAW,CAAC,CAoEtB;AAED,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
@@ -5,8 +5,10 @@ import { YouTubeError } from './errors.js';
5
5
  import { requireFfmpeg } from './preflight.js';
6
6
  import { resolveOutputDir } from './validate.js';
7
7
  import { TIMEOUTS, runYtDlp } from './ytdlp.js';
8
+ import { AFTER_MOVE_TEMPLATE, readAfterMove } from './youtube-download.js';
8
9
  import { formatSrtTimestamp } from './transcript.js';
9
10
  import { getChapters, getVideoInfo } from './youtube.js';
11
+ import { resolveChapter } from './chapters.js';
10
12
  import { dataDir } from './paths.js';
11
13
  /**
12
14
  * Pulling usable excerpts out of a video for editing.
@@ -53,20 +55,7 @@ export async function resolveRange(video, options) {
53
55
  const info = await getVideoInfo(video);
54
56
  let range;
55
57
  if (options.chapter !== undefined) {
56
- const chapters = await getChapters(video);
57
- if (chapters.length === 0) {
58
- throw new YouTubeError('NOT_FOUND', 'This video has no chapters.', {
59
- nextStep: 'Pass start and end instead.',
60
- });
61
- }
62
- const wanted = options.chapter.toLowerCase();
63
- const match = chapters.find((chapter) => chapter.title.toLowerCase() === wanted) ??
64
- chapters.find((chapter) => chapter.title.toLowerCase().includes(wanted));
65
- if (!match) {
66
- throw new YouTubeError('NOT_FOUND', `No chapter matches "${options.chapter}".`, {
67
- nextStep: `Available chapters: ${chapters.map((chapter) => chapter.title).join(', ')}`,
68
- });
69
- }
58
+ const match = resolveChapter(await getChapters(video), options.chapter, 'Pass start and end instead.');
70
59
  range = { start: match.startTime, end: match.endTime };
71
60
  }
72
61
  else {
@@ -123,18 +112,18 @@ export async function extractClip(video, rangeOptions, options) {
123
112
  else {
124
113
  args.push('--merge-output-format', options.container);
125
114
  }
126
- args.push(url);
127
- await runYtDlp(args, {
115
+ // The after_move stage fires once post-processing has produced the final
116
+ // file, so the path it prints is the real one — extension included — and
117
+ // nothing has to be spawned a second time to learn it.
118
+ args.push('--print', AFTER_MOVE_TEMPLATE);
119
+ const stdout = await runYtDlp(args, {
128
120
  label: 'extract_clip',
129
121
  timeoutMs: TIMEOUTS.download,
130
122
  retry: false,
123
+ target: url,
131
124
  });
132
- const filenameOutput = await runYtDlp([...args.slice(0, -1), '--print', 'filename', '--skip-download', url], { label: 'extract_clip (filename)' });
133
125
  const extension = options.audioOnly ? (options.audioFormat ?? 'mp3') : options.container;
134
- const reported = filenameOutput.trim();
135
- const filePath = reported === ''
136
- ? join(targetDir, `${stem}.${extension}`)
137
- : replaceExtension(reported, extension);
126
+ const filePath = stdout.trim() === '' ? join(targetDir, `${stem}.${extension}`) : readAfterMove(stdout).filepath;
138
127
  return {
139
128
  videoId,
140
129
  title,
@@ -144,9 +133,6 @@ export async function extractClip(video, rangeOptions, options) {
144
133
  duration: range.end - range.start,
145
134
  };
146
135
  }
147
- function replaceExtension(path, extension) {
148
- return path.replace(/\.[^./\\]+$/, `.${extension}`);
149
- }
150
136
  /**
151
137
  * Grab a single still.
152
138
  *
@@ -162,12 +148,16 @@ export async function extractFrame(video, timestamp, options) {
162
148
  }
163
149
  await mkdir(targetDir, { recursive: true });
164
150
  const url = `https://www.youtube.com/watch?v=${info.id}`;
165
- const mediaUrl = (await runYtDlp(['-f', 'bestvideo[height<=1080]/best', '--get-url', '--no-playlist', url], {
151
+ const mediaUrl = (await runYtDlp(['-f', 'bestvideo[height<=1080]/best', '--get-url', '--no-playlist'], {
166
152
  label: 'extract_frame (resolve)',
153
+ target: url,
167
154
  }))
168
155
  .trim()
169
156
  .split('\n')[0];
170
- if (mediaUrl === undefined || mediaUrl === '') {
157
+ // ffmpeg accepts file:, pipe:, concat: and more as inputs; the only thing
158
+ // yt-dlp should have resolved is an https media URL, and the allowlist
159
+ // below makes ffmpeg refuse anything else even if it had.
160
+ if (!mediaUrl?.startsWith('https://')) {
171
161
  throw new YouTubeError('YTDLP_FAILED', 'Could not resolve a video stream for this video.', {
172
162
  nextStep: 'Check the video is playable, or call check_health.',
173
163
  });
@@ -175,6 +165,8 @@ export async function extractFrame(video, timestamp, options) {
175
165
  const filePath = join(targetDir, `${safeStem(info.title, info.id)} [${Math.round(timestamp)}s].${options.format}`);
176
166
  try {
177
167
  await execa('ffmpeg', [
168
+ '-protocol_whitelist',
169
+ 'https,tls,tcp,crypto',
178
170
  // -ss before -i seeks without decoding everything up to that point.
179
171
  '-ss',
180
172
  String(timestamp),
@@ -1 +1 @@
1
- {"version":3,"file":"clips.js","sourceRoot":"","sources":["../../src/utils/clips.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC;;;;;;;GAOG;AAEH,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AACnC,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AACrC,MAAM,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;AAgB3C;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,QAAgB;IACtD,MAAM,OAAO,GAAG,KAAK;QACnB,yEAAyE;QACzE,qEAAqE;SACpE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;QACrB,sEAAsE;QACtE,uEAAuE;SACtE,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;SACtB,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;QAC9B,kFAAkF;SACjF,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;SACnB,IAAI,EAAE;SACN,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,IAAI,EAAE,CAAC;IAEV,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;AAC7C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAa,EACb,OAA2D;IAE3D,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC;IAEvC,IAAI,KAAgB,CAAC;IAErB,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,6BAA6B,EAAE;gBACjE,QAAQ,EAAE,6BAA6B;aACxC,CAAC,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QAC7C,MAAM,KAAK,GACT,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;YAClE,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAE3E,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,uBAAuB,OAAO,CAAC,OAAO,IAAI,EAAE;gBAC9E,QAAQ,EAAE,uBAAuB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;aACvF,CAAC,CAAC;QACL,CAAC;QACD,KAAK,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;IACzD,CAAC;SAAM,CAAC;QACN,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC7D,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,gDAAgD,EAAE;gBACxF,QAAQ,EAAE,6EAA6E;aACxF,CAAC,CAAC;QACL,CAAC;QACD,KAAK,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;IACrD,CAAC;IAED,IAAI,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC7B,MAAM,IAAI,YAAY,CAAC,eAAe,EAAE,+BAA+B,CAAC,CAAC;IAC3E,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtD,MAAM,IAAI,YAAY,CACpB,eAAe,EACf,UAAU,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,8BAA8B,IAAI,CAAC,QAAQ,UAAU,EACtF,EAAE,QAAQ,EAAE,qCAAqC,EAAE,CACpD,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,+BAA+B;IAC/B,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QACnD,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC3C,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;AACjF,CAAC;AAED,SAAS,UAAU,CAAC,KAAgB;IAClC,OAAO,IAAI,kBAAkB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;AACpH,CAAC;AAYD,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAa,EACb,YAAgE,EAChE,OAA2B;IAE3B,0EAA0E;IAC1E,2DAA2D;IAC3D,MAAM,aAAa,CAAC,iBAAiB,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAEjE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAC1E,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,MAAM,IAAI,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;IACjG,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,UAAU,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,mCAAmC,OAAO,EAAE,CAAC;IAEzD,MAAM,IAAI,GAAG;QACX,IAAI;QACJ,OAAO,CAAC,cAAc;QACtB,qBAAqB;QACrB,UAAU,CAAC,KAAK,CAAC;QACjB,IAAI;QACJ,cAAc;QACd,eAAe;KAChB,CAAC;IAEF,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;QACxB,4EAA4E;QAC5E,uDAAuD;QACvD,IAAI,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,gBAAgB,EAAE,OAAO,CAAC,WAAW,IAAI,KAAK,CAAC,CAAC;IAC/E,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,IAAI,CAAC,uBAAuB,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,CAAC;IAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEf,MAAM,QAAQ,CAAC,IAAI,EAAE;QACnB,KAAK,EAAE,cAAc;QACrB,SAAS,EAAE,QAAQ,CAAC,QAAQ;QAC5B,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,MAAM,QAAQ,CACnC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,CAAC,EACrE,EAAE,KAAK,EAAE,yBAAyB,EAAE,CACrC,CAAC;IAEF,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IACzF,MAAM,QAAQ,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC;IACvC,MAAM,QAAQ,GACZ,QAAQ,KAAK,EAAE;QACb,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,IAAI,SAAS,EAAE,CAAC;QACzC,CAAC,CAAC,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAE5C,OAAO;QACL,OAAO;QACP,KAAK;QACL,QAAQ;QACR,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,QAAQ,EAAE,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK;KAClC,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAE,SAAiB;IACvD,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,SAAS,EAAE,CAAC,CAAC;AACtD,CAAC;AASD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAa,EACb,SAAiB,EACjB,OAAsD;IAEtD,MAAM,aAAa,CAAC,kBAAkB,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAElE,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC;IAEvC,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,SAAS,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QACpD,MAAM,IAAI,YAAY,CACpB,eAAe,EACf,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,6BAA6B,IAAI,CAAC,QAAQ,UAAU,EAC5E,EAAE,QAAQ,EAAE,oCAAoC,EAAE,CACnD,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,MAAM,GAAG,GAAG,mCAAmC,IAAI,CAAC,EAAE,EAAE,CAAC;IACzD,MAAM,QAAQ,GAAG,CACf,MAAM,QAAQ,CAAC,CAAC,IAAI,EAAE,8BAA8B,EAAE,WAAW,EAAE,eAAe,EAAE,GAAG,CAAC,EAAE;QACxF,KAAK,EAAE,yBAAyB;KACjC,CAAC,CACH;SACE,IAAI,EAAE;SACN,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAElB,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QAC9C,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,kDAAkD,EAAE;YACzF,QAAQ,EAAE,oDAAoD;SAC/D,CAAC,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CACnB,SAAS,EACT,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,OAAO,CAAC,MAAM,EAAE,CACjF,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,KAAK,CACT,QAAQ,EACR;YACE,oEAAoE;YACpE,KAAK;YACL,MAAM,CAAC,SAAS,CAAC;YACjB,IAAI;YACJ,QAAQ;YACR,WAAW;YACX,GAAG;YACH,MAAM;YACN,GAAG;YACH,IAAI;YACJ,QAAQ;SACT,EACD,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAE,CACjC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,YAAY,CAAC,cAAc,EAAE,sCAAsC,EAAE;YAC7E,QAAQ,EAAE,2EAA2E;YACrF,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,aAAa,CAAC;AACvB,CAAC"}
1
+ {"version":3,"file":"clips.js","sourceRoot":"","sources":["../../src/utils/clips.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ import { z } from 'zod';
3
+ import type { CommentThread } from './comment-threads.js';
4
+ /**
5
+ * Comments on disk.
6
+ *
7
+ * Upserted on the comment id, never INSERT OR REPLACE: replace deletes and
8
+ * reinserts, which changes the rowid the external-content FTS index is keyed
9
+ * on. Upsert keeps it and fires the update trigger, so a re-harvest costs
10
+ * network and never data.
11
+ */
12
+ export declare const COMMENT_COMMIT_BATCH = 2000;
13
+ declare const commentRowSchema: z.ZodObject<{
14
+ comment_id: z.ZodString;
15
+ video_id: z.ZodString;
16
+ parent_id: z.ZodNullable<z.ZodString>;
17
+ text: z.ZodString;
18
+ like_count: z.ZodNumber;
19
+ author: z.ZodString;
20
+ author_id: z.ZodNullable<z.ZodString>;
21
+ author_is_uploader: z.ZodEffects<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<1>]>, boolean, 0 | 1>;
22
+ author_is_verified: z.ZodEffects<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<1>]>, boolean, 0 | 1>;
23
+ is_pinned: z.ZodEffects<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<1>]>, boolean, 0 | 1>;
24
+ is_favorited: z.ZodEffects<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<1>]>, boolean, 0 | 1>;
25
+ published_at: z.ZodNullable<z.ZodNumber>;
26
+ }, "strip", z.ZodTypeAny, {
27
+ author: string;
28
+ text: string;
29
+ like_count: number;
30
+ author_id: string | null;
31
+ author_is_uploader: boolean;
32
+ author_is_verified: boolean;
33
+ is_pinned: boolean;
34
+ is_favorited: boolean;
35
+ comment_id: string;
36
+ video_id: string;
37
+ parent_id: string | null;
38
+ published_at: number | null;
39
+ }, {
40
+ author: string;
41
+ text: string;
42
+ like_count: number;
43
+ author_id: string | null;
44
+ author_is_uploader: 0 | 1;
45
+ author_is_verified: 0 | 1;
46
+ is_pinned: 0 | 1;
47
+ is_favorited: 0 | 1;
48
+ comment_id: string;
49
+ video_id: string;
50
+ parent_id: string | null;
51
+ published_at: number | null;
52
+ }>;
53
+ export type StoredComment = z.infer<typeof commentRowSchema>;
54
+ /** Writes threads and their replies. The caller owns the transaction. */
55
+ export declare function saveThreads(database: DatabaseSync, videoId: string, threads: CommentThread[], now?: number): number;
56
+ export interface CommentQuery {
57
+ videoId?: string;
58
+ channelId?: string;
59
+ match?: string;
60
+ authorId?: string;
61
+ minLikes?: number;
62
+ topLevelOnly?: boolean;
63
+ threadOf?: string;
64
+ order?: 'likes' | 'newest' | 'oldest' | 'relevance';
65
+ limit?: number;
66
+ offset?: number;
67
+ }
68
+ export declare function queryComments(database: DatabaseSync, query: CommentQuery): StoredComment[];
69
+ export declare function countComments(database: DatabaseSync, query: CommentQuery): number;
70
+ export {};
71
+ //# sourceMappingURL=comment-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-store.d.ts","sourceRoot":"","sources":["../../src/utils/comment-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAmB,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG3E;;;;;;;GAOG;AAEH,eAAO,MAAM,oBAAoB,OAAQ,CAAC;AAe1C,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAapB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAuB7D,yEAAyE;AACzE,wBAAgB,WAAW,CACzB,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,aAAa,EAAE,EACxB,GAAG,SAAa,GACf,MAAM,CAcR;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA8CD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,aAAa,EAAE,CAoB1F;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,CAQjF"}
@@ -0,0 +1,129 @@
1
+ import { z } from 'zod';
2
+ import { queryRows, sqliteBoolean } from './store-rows.js';
3
+ /**
4
+ * Comments on disk.
5
+ *
6
+ * Upserted on the comment id, never INSERT OR REPLACE: replace deletes and
7
+ * reinserts, which changes the rowid the external-content FTS index is keyed
8
+ * on. Upsert keeps it and fires the update trigger, so a re-harvest costs
9
+ * network and never data.
10
+ */
11
+ export const COMMENT_COMMIT_BATCH = 2_000;
12
+ const UPSERT = `
13
+ INSERT INTO comment (
14
+ comment_id, video_id, parent_id, text, like_count, author, author_id, author_url,
15
+ author_thumbnail, author_is_uploader, author_is_verified, is_pinned, is_favorited,
16
+ time_text, published_at, harvested_at
17
+ ) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
18
+ ON CONFLICT(comment_id) DO UPDATE SET
19
+ text = excluded.text,
20
+ like_count = excluded.like_count,
21
+ is_pinned = excluded.is_pinned,
22
+ is_favorited = excluded.is_favorited,
23
+ harvested_at = excluded.harvested_at`;
24
+ const commentRowSchema = z.object({
25
+ comment_id: z.string(),
26
+ video_id: z.string(),
27
+ parent_id: z.string().nullable(),
28
+ text: z.string(),
29
+ like_count: z.number(),
30
+ author: z.string(),
31
+ author_id: z.string().nullable(),
32
+ author_is_uploader: sqliteBoolean,
33
+ author_is_verified: sqliteBoolean,
34
+ is_pinned: sqliteBoolean,
35
+ is_favorited: sqliteBoolean,
36
+ published_at: z.number().nullable(),
37
+ });
38
+ function bind(comment, videoId, now) {
39
+ return [
40
+ comment.id,
41
+ videoId,
42
+ comment.parentId,
43
+ comment.text,
44
+ comment.likeCount,
45
+ comment.author,
46
+ comment.authorId ?? null,
47
+ comment.authorUrl ?? null,
48
+ comment.authorThumbnailUrl ?? null,
49
+ Number(comment.authorIsUploader),
50
+ Number(comment.authorIsVerified),
51
+ Number(comment.isPinned),
52
+ Number(comment.isFavorited),
53
+ comment.timeText ?? null,
54
+ comment.timestamp ?? null,
55
+ now,
56
+ ];
57
+ }
58
+ /** Writes threads and their replies. The caller owns the transaction. */
59
+ export function saveThreads(database, videoId, threads, now = Date.now()) {
60
+ const statement = database.prepare(UPSERT);
61
+ let written = 0;
62
+ for (const thread of threads) {
63
+ statement.run(...bind(thread.comment, videoId, now));
64
+ written += 1;
65
+ for (const reply of thread.replies) {
66
+ statement.run(...bind(reply, videoId, now));
67
+ written += 1;
68
+ }
69
+ }
70
+ return written;
71
+ }
72
+ function buildWhere(query) {
73
+ const clauses = [];
74
+ const params = [];
75
+ if (query.videoId !== undefined) {
76
+ clauses.push('c.video_id = ?');
77
+ params.push(query.videoId);
78
+ }
79
+ if (query.channelId !== undefined) {
80
+ clauses.push('c.video_id IN (SELECT video_id FROM video WHERE channel_id = ?)');
81
+ params.push(query.channelId);
82
+ }
83
+ if (query.authorId !== undefined) {
84
+ clauses.push('(c.author_id = ? OR c.author = ?)');
85
+ params.push(query.authorId, query.authorId);
86
+ }
87
+ if (query.threadOf !== undefined) {
88
+ clauses.push('(c.comment_id = ? OR c.parent_id = ?)');
89
+ params.push(query.threadOf, query.threadOf);
90
+ }
91
+ if (query.topLevelOnly === true)
92
+ clauses.push('c.parent_id IS NULL');
93
+ if (query.minLikes !== undefined && query.minLikes > 0) {
94
+ clauses.push('c.like_count >= ?');
95
+ params.push(query.minLikes);
96
+ }
97
+ if (query.match !== undefined && query.match !== '') {
98
+ // Bound, never concatenated: `match` is caller text and FTS5 has its own
99
+ // expression syntax that must not reach the SQL parser.
100
+ clauses.push('c.rowid IN (SELECT rowid FROM comment_fts WHERE comment_fts MATCH ?)');
101
+ params.push(query.match);
102
+ }
103
+ return { sql: clauses.length === 0 ? '' : ` WHERE ${clauses.join(' AND ')}`, params };
104
+ }
105
+ const ORDER = {
106
+ likes: 'c.like_count DESC, c.comment_id',
107
+ newest: 'c.published_at DESC, c.comment_id',
108
+ oldest: 'c.published_at ASC, c.comment_id',
109
+ // Threads read in reply order; relevance needs the FTS rank, which only
110
+ // exists when a match was given, so it falls back to likes.
111
+ relevance: 'c.like_count DESC, c.comment_id',
112
+ };
113
+ export function queryComments(database, query) {
114
+ const where = buildWhere(query);
115
+ const order = query.threadOf === undefined
116
+ ? ORDER[query.order ?? 'likes']
117
+ : 'c.parent_id NULLS FIRST, c.published_at';
118
+ return queryRows(database.prepare(`SELECT c.comment_id, c.video_id, c.parent_id, c.text, c.like_count, c.author,
119
+ c.author_id, c.author_is_uploader, c.author_is_verified, c.is_pinned,
120
+ c.is_favorited, c.published_at
121
+ FROM comment c${where.sql}
122
+ ORDER BY ${order} LIMIT ? OFFSET ?`), commentRowSchema, ...where.params, query.limit ?? 25, query.offset ?? 0);
123
+ }
124
+ export function countComments(database, query) {
125
+ const where = buildWhere(query);
126
+ const rows = queryRows(database.prepare(`SELECT COUNT(*) AS n FROM comment c${where.sql}`), z.object({ n: z.number() }), ...where.params);
127
+ return rows[0]?.n ?? 0;
128
+ }
129
+ //# sourceMappingURL=comment-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-store.js","sourceRoot":"","sources":["../../src/utils/comment-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { z } from 'zod';
2
+ import type { commentRowSchema } from './youtube-schemas.js';
3
+ /**
4
+ * Rebuilding threads from yt-dlp's flat comment array.
5
+ *
6
+ * yt-dlp returns replies alongside their parents and marks each with a
7
+ * `parent` of `root` or the parent's id. The previous implementation filtered
8
+ * the replies away *after* paying to fetch them, which is the most expensive
9
+ * possible way to have no replies.
10
+ */
11
+ export type CommentRow = z.infer<typeof commentRowSchema>;
12
+ export interface ThreadedComment {
13
+ id: string;
14
+ parentId: string | null;
15
+ author: string;
16
+ authorId?: string;
17
+ authorUrl?: string;
18
+ authorThumbnailUrl?: string;
19
+ authorIsUploader: boolean;
20
+ authorIsVerified: boolean;
21
+ text: string;
22
+ likeCount: number;
23
+ isPinned: boolean;
24
+ isFavorited: boolean;
25
+ timestamp?: number;
26
+ publishedAt?: string;
27
+ timeText?: string;
28
+ }
29
+ export interface CommentThread {
30
+ comment: ThreadedComment;
31
+ replies: ThreadedComment[];
32
+ /** Replies present in THIS extraction, not YouTube's own figure. */
33
+ replyCount: number;
34
+ }
35
+ export interface ThreadedComments {
36
+ threads: CommentThread[];
37
+ rootCount: number;
38
+ replyCount: number;
39
+ /** Replies whose parent was cut by a cap. Kept, and counted, never dropped. */
40
+ orphanCount: number;
41
+ }
42
+ /**
43
+ * Groups rows into threads, in yt-dlp's own order.
44
+ *
45
+ * A reply whose parent is missing — because `max_parents` truncated the set —
46
+ * is promoted to a root rather than discarded, keeping its `parentId` so the
47
+ * gap stays visible. Silently dropping data is the bug this whole release is
48
+ * about.
49
+ */
50
+ export declare function toThreads(rows: CommentRow[]): ThreadedComments;
51
+ //# sourceMappingURL=comment-threads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-threads.d.ts","sourceRoot":"","sources":["../../src/utils/comment-threads.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;;;;GAOG;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,oEAAoE;IACpE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;CACrB;AAiCD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,gBAAgB,CA0C9D"}
@@ -0,0 +1,76 @@
1
+ function toComment(row, index) {
2
+ const parent = row.parent ?? 'root';
3
+ const timestamp = row.timestamp ?? undefined;
4
+ // yt-dlp writes null for an absent field; normalised to undefined once here
5
+ // so every spread below is a plain `=== undefined` check.
6
+ const authorId = row.author_id ?? undefined;
7
+ const authorUrl = row.author_url ?? undefined;
8
+ const authorThumbnail = row.author_thumbnail ?? undefined;
9
+ const timeText = row._time_text ?? undefined;
10
+ return {
11
+ // yt-dlp always supplies an id; the fallback keeps a malformed row
12
+ // addressable rather than silently merging it with another.
13
+ id: row.id ?? `unknown-${String(index)}`,
14
+ parentId: parent === 'root' ? null : parent,
15
+ author: row.author ?? 'Unknown',
16
+ ...(authorId === undefined ? {} : { authorId }),
17
+ ...(authorUrl === undefined ? {} : { authorUrl }),
18
+ ...(authorThumbnail === undefined ? {} : { authorThumbnailUrl: authorThumbnail }),
19
+ authorIsUploader: row.author_is_uploader ?? false,
20
+ authorIsVerified: row.author_is_verified ?? false,
21
+ text: row.text ?? '',
22
+ likeCount: row.like_count ?? 0,
23
+ isPinned: row.is_pinned ?? false,
24
+ isFavorited: row.is_favorited ?? false,
25
+ ...(timestamp === undefined ? {} : { timestamp }),
26
+ ...(timestamp === undefined ? {} : { publishedAt: new Date(timestamp * 1000).toISOString() }),
27
+ ...(timeText === undefined ? {} : { timeText }),
28
+ };
29
+ }
30
+ /**
31
+ * Groups rows into threads, in yt-dlp's own order.
32
+ *
33
+ * A reply whose parent is missing — because `max_parents` truncated the set —
34
+ * is promoted to a root rather than discarded, keeping its `parentId` so the
35
+ * gap stays visible. Silently dropping data is the bug this whole release is
36
+ * about.
37
+ */
38
+ export function toThreads(rows) {
39
+ const comments = rows.map(toComment);
40
+ const threads = [];
41
+ const threadById = new Map();
42
+ let replyCount = 0;
43
+ let orphanCount = 0;
44
+ for (const comment of comments) {
45
+ if (comment.parentId === null) {
46
+ const thread = { comment, replies: [], replyCount: 0 };
47
+ threads.push(thread);
48
+ threadById.set(comment.id, thread);
49
+ }
50
+ }
51
+ for (const comment of comments) {
52
+ if (comment.parentId === null)
53
+ continue;
54
+ const thread = threadById.get(comment.parentId);
55
+ if (thread === undefined) {
56
+ // The parent was cut by a cap. Keep the reply as its own thread so the
57
+ // text survives, and count it so the caller knows threads are partial.
58
+ orphanCount += 1;
59
+ threads.push({ comment, replies: [], replyCount: 0 });
60
+ continue;
61
+ }
62
+ thread.replies.push(comment);
63
+ thread.replyCount += 1;
64
+ replyCount += 1;
65
+ }
66
+ for (const thread of threads) {
67
+ thread.replies.sort((left, right) => (left.timestamp ?? 0) - (right.timestamp ?? 0));
68
+ }
69
+ return {
70
+ threads,
71
+ rootCount: threads.length - orphanCount,
72
+ replyCount,
73
+ orphanCount,
74
+ };
75
+ }
76
+ //# sourceMappingURL=comment-threads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-threads.js","sourceRoot":"","sources":["../../src/utils/comment-threads.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Coverage } from '../harvest-schemas.js';
2
+ export declare function renderCoverage(coverage: Coverage): string;
3
+ //# sourceMappingURL=coverage-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverage-text.d.ts","sourceRoot":"","sources":["../../src/utils/coverage-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AA8CtD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CA6BzD"}