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,89 @@
1
+ import { inBatches } from './batches.js';
2
+ import { reportProgress, throwIfAborted } from './context.js';
3
+ import { channelImage, fetchOne, reconciled } from './thumbnail-entry.js';
4
+ import { THUMBNAIL_MANIFEST_VERSION, computeThumbnailStats, writeThumbnailManifest, } from './thumbnail-store.js';
5
+ import { concurrencyState } from './ytdlp.js';
6
+ /**
7
+ * Fetching a channel's thumbnails, resumably.
8
+ *
9
+ * Shaped like a brain build, and for the same reason: several hundred images
10
+ * is several hundred requests, so the job gets interrupted — cancelled by the
11
+ * client, throttled by YouTube, or killed with the editor. Per-image state
12
+ * rather than one flag, checkpoints rather than one write at the end, a
13
+ * failed image costing only itself, and a second call that continues.
14
+ */
15
+ export const CHECKPOINT_EVERY_ITEMS = 20;
16
+ export const RATE_LIMIT_TOLERANCE = 3;
17
+ export async function fetchChannelThumbnails(options) {
18
+ const { channel, listings, existing, quality } = options;
19
+ const { channelId } = channel;
20
+ const createdAt = existing?.createdAt ?? new Date().toISOString();
21
+ const tabs = [...new Set([...(existing?.tabs ?? []), ...listings.map((l) => l.tab)])];
22
+ const entries = await reconciled(channelId, existing, listings);
23
+ const images = { avatar: existing?.avatar, banner: existing?.banner };
24
+ const save = async () => {
25
+ const manifest = {
26
+ version: THUMBNAIL_MANIFEST_VERSION,
27
+ channel,
28
+ tabs,
29
+ quality,
30
+ createdAt,
31
+ updatedAt: new Date().toISOString(),
32
+ ...(images.avatar === undefined ? {} : { avatar: images.avatar }),
33
+ ...(images.banner === undefined ? {} : { banner: images.banner }),
34
+ videos: Object.fromEntries(entries),
35
+ stats: computeThumbnailStats([...entries.values()]),
36
+ };
37
+ await writeThumbnailManifest(manifest);
38
+ return manifest;
39
+ };
40
+ images.avatar = await channelImage(channelId, 'avatar', channel.avatarUrl, images.avatar);
41
+ images.banner = await channelImage(channelId, 'banner', channel.bannerUrl, images.banner);
42
+ const outstanding = [...entries.values()].filter((e) => e.state !== 'saved');
43
+ const failures = [];
44
+ let attempted = 0;
45
+ let fetched = 0;
46
+ let consecutiveThrottles = 0;
47
+ let stopReason;
48
+ try {
49
+ await inBatches(outstanding, concurrencyState().limit, async (entry) => {
50
+ if (stopReason !== undefined)
51
+ return;
52
+ throwIfAborted();
53
+ const updated = await fetchOne(channelId, entry, quality);
54
+ entries.set(entry.videoId, updated);
55
+ attempted++;
56
+ if (updated.state === 'saved')
57
+ fetched++;
58
+ else
59
+ failures.push({
60
+ videoId: entry.videoId,
61
+ tab: entry.tab,
62
+ error: updated.error ?? 'FETCH_FAILED',
63
+ });
64
+ consecutiveThrottles = updated.error === 'RATE_LIMITED' ? consecutiveThrottles + 1 : 0;
65
+ if (consecutiveThrottles >= RATE_LIMIT_TOLERANCE)
66
+ stopReason = 'YouTube is rate limiting image downloads.';
67
+ reportProgress(attempted, outstanding.length, `Fetched ${attempted} of ${outstanding.length}`);
68
+ if (attempted % CHECKPOINT_EVERY_ITEMS === 0)
69
+ await save();
70
+ });
71
+ }
72
+ catch (error) {
73
+ // A cancelled job keeps what it fetched since the last checkpoint.
74
+ await save();
75
+ throw error;
76
+ }
77
+ const manifest = await save();
78
+ return {
79
+ manifest,
80
+ considered: entries.size,
81
+ fetched,
82
+ skipped: entries.size - outstanding.length,
83
+ failed: failures.length,
84
+ failures,
85
+ stoppedEarly: stopReason !== undefined,
86
+ ...(stopReason === undefined ? {} : { stopReason }),
87
+ };
88
+ }
89
+ //# sourceMappingURL=thumbnail-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thumbnail-fetch.js","sourceRoot":"","sources":["../../src/utils/thumbnail-fetch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { type ImageProbe } from './image-dimensions.js';
2
+ import type { ListedImage } from './flat-listing.js';
3
+ import type { ThumbnailQuality } from '../thumbnail-schemas.js';
4
+ /**
5
+ * Getting the best thumbnail YouTube will serve for one video.
6
+ *
7
+ * A flat channel listing offers each video's thumbnail at up to 720 pixels
8
+ * wide (verified against yt-dlp 2026.07.04). YouTube also serves a larger
9
+ * `maxresdefault.jpg` for many videos — the probed video returned one at
10
+ * 1280 wide — but not, as far as this code knows, for every video, and a
11
+ * missing one is not guaranteed to be a 404. So the ladder tries it and then
12
+ * checks what came back: an image is accepted only if it decodes and is wider
13
+ * than the listed one. Anything else falls through to the next rung. No
14
+ * behaviour of YouTube's is assumed that a rung could not verify for itself.
15
+ */
16
+ export interface Rung {
17
+ variant: string;
18
+ url: string;
19
+ /** Accept the image only if it decodes wider than this. */
20
+ mustExceedWidth?: number;
21
+ }
22
+ export interface Climbed {
23
+ bytes: Buffer;
24
+ probe: ImageProbe;
25
+ rung: Rung;
26
+ }
27
+ /**
28
+ * The rungs for one video, best first.
29
+ *
30
+ * Shorts are listed with portrait thumbnails; whether `maxresdefault` exists
31
+ * for a short, or is portrait when it does, is not verified, so a short uses
32
+ * its listed image and the small landscape fallback only.
33
+ */
34
+ export declare function videoRungs(videoId: string, listed: ListedImage | undefined, isShort: boolean, quality: ThumbnailQuality): Rung[];
35
+ /**
36
+ * Fetch rungs in order and return the first acceptable image.
37
+ *
38
+ * A rung is accepted when it downloads, decodes as an image this server reads,
39
+ * and — where the rung demands it — is wider than the listed image. A 404 or
40
+ * an unacceptable image moves to the next rung; rate limiting, a timeout or a
41
+ * cancelled request stops the climb immediately.
42
+ */
43
+ export declare function climb(rungs: Rung[], maxBytes: number): Promise<Climbed>;
44
+ //# sourceMappingURL=thumbnail-ladder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thumbnail-ladder.d.ts","sourceRoot":"","sources":["../../src/utils/thumbnail-ladder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,IAAI;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,2DAA2D;IAC3D,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,IAAI,CAAC;CACZ;AAQD;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,gBAAgB,GACxB,IAAI,EAAE,CAaR;AAKD;;;;;;;GAOG;AACH,wBAAsB,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgC7E"}
@@ -0,0 +1,68 @@
1
+ import { probeImage } from './image-dimensions.js';
2
+ import { fetchImage } from './image-fetch.js';
3
+ import { YouTubeError } from './errors.js';
4
+ const named = (videoId, variant, mustExceedWidth) => ({
5
+ variant,
6
+ url: `https://i.ytimg.com/vi/${videoId}/${variant}.jpg`,
7
+ ...(mustExceedWidth === undefined ? {} : { mustExceedWidth }),
8
+ });
9
+ /**
10
+ * The rungs for one video, best first.
11
+ *
12
+ * Shorts are listed with portrait thumbnails; whether `maxresdefault` exists
13
+ * for a short, or is portrait when it does, is not verified, so a short uses
14
+ * its listed image and the small landscape fallback only.
15
+ */
16
+ export function videoRungs(videoId, listed, isShort, quality) {
17
+ const listedRung = listed === undefined ? [] : [{ variant: 'listed', url: listed.url }];
18
+ const fallback = named(videoId, 'hqdefault');
19
+ if (isShort)
20
+ return [...listedRung, fallback];
21
+ if (quality === 'listed')
22
+ return [...listedRung, fallback];
23
+ const best = named(videoId, 'maxresdefault', listed?.width ?? 200);
24
+ // With no listing to beat, the smaller named sizes are worth trying too.
25
+ return listed === undefined
26
+ ? [best, named(videoId, 'sddefault'), fallback]
27
+ : [best, ...listedRung, fallback];
28
+ }
29
+ /** The reasons to stop climbing: they are about the client, not the rung. */
30
+ const ABORTING = new Set(['RATE_LIMITED', 'TIMEOUT', 'CANCELLED']);
31
+ /**
32
+ * Fetch rungs in order and return the first acceptable image.
33
+ *
34
+ * A rung is accepted when it downloads, decodes as an image this server reads,
35
+ * and — where the rung demands it — is wider than the listed image. A 404 or
36
+ * an unacceptable image moves to the next rung; rate limiting, a timeout or a
37
+ * cancelled request stops the climb immediately.
38
+ */
39
+ export async function climb(rungs, maxBytes) {
40
+ let last;
41
+ for (const rung of rungs) {
42
+ try {
43
+ const { bytes } = await fetchImage(rung.url, { maxBytes });
44
+ const probe = probeImage(bytes);
45
+ if (probe === undefined) {
46
+ last = new YouTubeError('FETCH_FAILED', 'The response was not an image this server can read.');
47
+ continue;
48
+ }
49
+ if (rung.mustExceedWidth !== undefined && probe.width <= rung.mustExceedWidth) {
50
+ last = new YouTubeError('NOT_FOUND', 'No larger image is available at that address.');
51
+ continue;
52
+ }
53
+ return { bytes, probe, rung };
54
+ }
55
+ catch (error) {
56
+ if (!(error instanceof YouTubeError))
57
+ throw error;
58
+ if (ABORTING.has(error.code))
59
+ throw error;
60
+ last = error;
61
+ }
62
+ }
63
+ throw (last ??
64
+ new YouTubeError('NOT_FOUND', 'No thumbnail address was available for this video.', {
65
+ nextStep: 'The listing carried no thumbnail; try again later.',
66
+ }));
67
+ }
68
+ //# sourceMappingURL=thumbnail-ladder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thumbnail-ladder.js","sourceRoot":"","sources":["../../src/utils/thumbnail-ladder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { ThumbnailTab } from '../thumbnail-schemas.js';
2
+ import type { TabListing } from './thumbnail-entry.js';
3
+ /**
4
+ * List each tab. The uploads tab must list — every channel has one, so a
5
+ * failure there is worth reporting. Whether yt-dlp lists an empty shorts or
6
+ * streams tab or refuses it is not verified, so those failures are recorded
7
+ * per tab and the run continues.
8
+ */
9
+ export declare function listTabs(channelUrl: string, tabs: ThumbnailTab[], maxVideos: number): Promise<{
10
+ listings: TabListing[];
11
+ tabErrors: {
12
+ tab: ThumbnailTab;
13
+ error: string;
14
+ }[];
15
+ }>;
16
+ //# sourceMappingURL=thumbnail-listing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thumbnail-listing.d.ts","sourceRoot":"","sources":["../../src/utils/thumbnail-listing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,YAAY,EAAE,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAAC,SAAS,EAAE;QAAE,GAAG,EAAE,YAAY,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAAC,CAkBxF"}
@@ -0,0 +1,26 @@
1
+ import { asYouTubeError } from './errors.js';
2
+ import { listVideos } from './youtube.js';
3
+ /**
4
+ * List each tab. The uploads tab must list — every channel has one, so a
5
+ * failure there is worth reporting. Whether yt-dlp lists an empty shorts or
6
+ * streams tab or refuses it is not verified, so those failures are recorded
7
+ * per tab and the run continues.
8
+ */
9
+ export async function listTabs(channelUrl, tabs, maxVideos) {
10
+ const base = channelUrl.replace(/\/+$/, '');
11
+ const listings = [];
12
+ const tabErrors = [];
13
+ for (const tab of tabs) {
14
+ try {
15
+ const videos = (await listVideos(`${base}/${tab}`, maxVideos)).slice(0, maxVideos);
16
+ listings.push({ tab, videos });
17
+ }
18
+ catch (error) {
19
+ if (tab === 'videos')
20
+ throw error;
21
+ tabErrors.push({ tab, error: asYouTubeError(error).code });
22
+ }
23
+ }
24
+ return { listings, tabErrors };
25
+ }
26
+ //# sourceMappingURL=thumbnail-listing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thumbnail-listing.js","sourceRoot":"","sources":["../../src/utils/thumbnail-listing.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAsB,MAAM,cAAc,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,UAAkB,EAClB,IAAoB,EACpB,SAAiB;IAEjB,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAClC,MAAM,SAAS,GAA2C,EAAE,CAAC;IAE7D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAoB,CAAC,MAAM,UAAU,CAAC,GAAG,IAAI,IAAI,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CACnF,CAAC,EACD,SAAS,CACV,CAAC;YACF,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;QACjC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,GAAG,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YAClC,SAAS,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { ImageFormat } from './image-dimensions.js';
2
+ import type { ThumbnailTab } from '../thumbnail-schemas.js';
3
+ /**
4
+ * Every file a channel's thumbnails are made of.
5
+ *
6
+ * A sibling of the brains rather than inside them: deleting a brain removes
7
+ * its whole directory, and a channel's images should outlive its corpus. Keyed
8
+ * on the channel id, validated once here; a filename is the video id, also
9
+ * validated here; and the tab and format are closed enums. Nothing a caller or
10
+ * a hand-edited manifest supplies reaches a path unchecked.
11
+ */
12
+ export declare function thumbnailsDir(): string;
13
+ export declare function thumbnailDir(channelId: string): string;
14
+ export declare function thumbnailManifestPath(channelId: string): string;
15
+ export declare function thumbnailLockPath(channelId: string): string;
16
+ export declare function channelImagePath(channelId: string, kind: 'avatar' | 'banner', format: ImageFormat): string;
17
+ export declare function videoThumbnailPath(channelId: string, tab: ThumbnailTab, videoId: string, format: ImageFormat): string;
18
+ /** The informational `file` field: where a file sits inside the channel directory. */
19
+ export declare function relativeFile(channelId: string, absolute: string): string;
20
+ //# sourceMappingURL=thumbnail-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thumbnail-paths.d.ts","sourceRoot":"","sources":["../../src/utils/thumbnail-paths.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D;;;;;;;;GAQG;AAEH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,QAAQ,GAAG,QAAQ,EACzB,MAAM,EAAE,WAAW,GAClB,MAAM,CAER;AAED,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,WAAW,GAClB,MAAM,CAER;AAED,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExE"}
@@ -0,0 +1,35 @@
1
+ import { join, relative } from 'node:path';
2
+ import { dataDir } from './paths.js';
3
+ import { assertChannelId, assertVideoId } from './validate.js';
4
+ /**
5
+ * Every file a channel's thumbnails are made of.
6
+ *
7
+ * A sibling of the brains rather than inside them: deleting a brain removes
8
+ * its whole directory, and a channel's images should outlive its corpus. Keyed
9
+ * on the channel id, validated once here; a filename is the video id, also
10
+ * validated here; and the tab and format are closed enums. Nothing a caller or
11
+ * a hand-edited manifest supplies reaches a path unchecked.
12
+ */
13
+ export function thumbnailsDir() {
14
+ return dataDir('thumbnails');
15
+ }
16
+ export function thumbnailDir(channelId) {
17
+ return join(thumbnailsDir(), assertChannelId(channelId));
18
+ }
19
+ export function thumbnailManifestPath(channelId) {
20
+ return join(thumbnailDir(channelId), 'manifest.json');
21
+ }
22
+ export function thumbnailLockPath(channelId) {
23
+ return join(thumbnailDir(channelId), 'fetch.lock');
24
+ }
25
+ export function channelImagePath(channelId, kind, format) {
26
+ return join(thumbnailDir(channelId), 'channel', `${kind}.${format}`);
27
+ }
28
+ export function videoThumbnailPath(channelId, tab, videoId, format) {
29
+ return join(thumbnailDir(channelId), tab, `${assertVideoId(videoId)}.${format}`);
30
+ }
31
+ /** The informational `file` field: where a file sits inside the channel directory. */
32
+ export function relativeFile(channelId, absolute) {
33
+ return relative(thumbnailDir(channelId), absolute);
34
+ }
35
+ //# sourceMappingURL=thumbnail-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thumbnail-paths.js","sourceRoot":"","sources":["../../src/utils/thumbnail-paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG/D;;;;;;;;GAQG;AAEH,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC,YAAY,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,OAAO,IAAI,CAAC,aAAa,EAAE,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,SAAiB;IACrD,OAAO,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,eAAe,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,OAAO,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,YAAY,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,SAAiB,EACjB,IAAyB,EACzB,MAAmB;IAEnB,OAAO,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,IAAI,MAAM,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,SAAiB,EACjB,GAAiB,EACjB,OAAe,EACf,MAAmB;IAEnB,OAAO,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;AACnF,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,YAAY,CAAC,SAAiB,EAAE,QAAgB;IAC9D,OAAO,QAAQ,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { type ChannelImage, type ThumbnailEntry, type ThumbnailManifest, type ThumbnailStats, type ThumbnailTab } from '../thumbnail-schemas.js';
2
+ /** The manifest of a channel's thumbnails: reading, writing, finding and forgetting it. */
3
+ export declare const THUMBNAIL_MANIFEST_VERSION = 1;
4
+ export declare function ensureThumbnailDirs(channelId: string, tabs: ThumbnailTab[]): Promise<string>;
5
+ export declare function readThumbnailManifest(channelId: string): Promise<ThumbnailManifest | undefined>;
6
+ export declare function requireThumbnailManifest(channelId: string): Promise<ThumbnailManifest>;
7
+ export declare function writeThumbnailManifest(manifest: ThumbnailManifest): Promise<void>;
8
+ /** Every thumbnail set on disk, newest first. Unreadable directories are skipped. */
9
+ export declare function listThumbnailManifests(): Promise<ThumbnailManifest[]>;
10
+ /** The thumbnail set for a channel named any way the caller likes, from disk alone. */
11
+ export declare function resolveThumbnails(channel: string): Promise<ThumbnailManifest>;
12
+ export declare function deleteThumbnails(channelId: string): Promise<boolean>;
13
+ /** Where a saved entry's file is, recomputed from validated parts; never read from the manifest. */
14
+ export declare function entryPath(channelId: string, entry: ThumbnailEntry): string | undefined;
15
+ export declare function channelImageFile(channelId: string, image: ChannelImage): string | undefined;
16
+ /** True when the file a saved record describes is on disk at the recorded size. */
17
+ export declare function isIntact(path: string | undefined, bytes: number | undefined): Promise<boolean>;
18
+ export declare function computeThumbnailStats(entries: ThumbnailEntry[]): ThumbnailStats;
19
+ //# sourceMappingURL=thumbnail-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thumbnail-store.d.ts","sourceRoot":"","sources":["../../src/utils/thumbnail-store.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,yBAAyB,CAAC;AAajC,2FAA2F;AAE3F,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,YAAY,EAAE,GACnB,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAExC;AAED,wBAAsB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAY5F;AAED,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAGvF;AAED,qFAAqF;AACrF,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAa3E;AAED,uFAAuF;AACvF,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAmBnF;AAED,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAK1E;AAED,oGAAoG;AACpG,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAGtF;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAG3F;AAED,mFAAmF;AACnF,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,cAAc,CA2B/E"}
@@ -0,0 +1,125 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readdir, rm, stat } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { thumbnailManifestSchema, } from '../thumbnail-schemas.js';
5
+ import { findByChannel } from './channel-lookup.js';
6
+ import { YouTubeError } from './errors.js';
7
+ import { readJsonFile, writeJsonAtomic } from './json-file.js';
8
+ import { ensurePrivateDir } from './paths.js';
9
+ import { channelImagePath, thumbnailDir, thumbnailManifestPath, thumbnailsDir, videoThumbnailPath, } from './thumbnail-paths.js';
10
+ /** The manifest of a channel's thumbnails: reading, writing, finding and forgetting it. */
11
+ export const THUMBNAIL_MANIFEST_VERSION = 1;
12
+ export async function ensureThumbnailDirs(channelId, tabs) {
13
+ const directory = await ensurePrivateDir(thumbnailDir(channelId));
14
+ await ensurePrivateDir(join(directory, 'channel'));
15
+ for (const tab of tabs)
16
+ await ensurePrivateDir(join(directory, tab));
17
+ return directory;
18
+ }
19
+ export async function readThumbnailManifest(channelId) {
20
+ return readJsonFile(thumbnailManifestPath(channelId), thumbnailManifestSchema);
21
+ }
22
+ export async function requireThumbnailManifest(channelId) {
23
+ const manifest = await readThumbnailManifest(channelId);
24
+ if (manifest === undefined) {
25
+ throw new YouTubeError('NOT_FOUND', `No thumbnails have been fetched for channel ${channelId}.`, {
26
+ nextStep: 'Call fetch_channel_thumbnails with the channel URL or handle.',
27
+ });
28
+ }
29
+ return manifest;
30
+ }
31
+ export async function writeThumbnailManifest(manifest) {
32
+ await ensurePrivateDir(thumbnailDir(manifest.channel.channelId));
33
+ await writeJsonAtomic(thumbnailManifestPath(manifest.channel.channelId), manifest);
34
+ }
35
+ /** Every thumbnail set on disk, newest first. Unreadable directories are skipped. */
36
+ export async function listThumbnailManifests() {
37
+ if (!existsSync(thumbnailsDir()))
38
+ return [];
39
+ const manifests = [];
40
+ for (const entry of await readdir(thumbnailsDir(), { withFileTypes: true })) {
41
+ if (!entry.isDirectory())
42
+ continue;
43
+ const manifest = await readJsonFile(join(thumbnailsDir(), entry.name, 'manifest.json'), thumbnailManifestSchema);
44
+ if (manifest !== undefined)
45
+ manifests.push(manifest);
46
+ }
47
+ return manifests.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
48
+ }
49
+ /** The thumbnail set for a channel named any way the caller likes, from disk alone. */
50
+ export async function resolveThumbnails(channel) {
51
+ const manifests = await listThumbnailManifests();
52
+ const [first, second] = findByChannel(manifests, channel);
53
+ if (first === undefined) {
54
+ const fetched = manifests.map((m) => m.channel.handle || m.channel.name);
55
+ throw new YouTubeError('NOT_FOUND', `No thumbnails have been fetched for "${channel}".`, {
56
+ nextStep: fetched.length === 0
57
+ ? 'Call fetch_channel_thumbnails with the channel URL or handle.'
58
+ : `Call fetch_channel_thumbnails to fetch them. Channels with thumbnails: ${fetched.join(', ')}.`,
59
+ });
60
+ }
61
+ if (second !== undefined) {
62
+ throw new YouTubeError('INVALID_INPUT', `"${channel}" matches more than one channel.`, {
63
+ nextStep: `Use the channel id instead: ${[first, second].map((m) => m.channel.channelId).join(', ')}.`,
64
+ });
65
+ }
66
+ return first;
67
+ }
68
+ export async function deleteThumbnails(channelId) {
69
+ const directory = thumbnailDir(channelId);
70
+ const existed = existsSync(directory);
71
+ await rm(directory, { recursive: true, force: true });
72
+ return existed;
73
+ }
74
+ /** Where a saved entry's file is, recomputed from validated parts; never read from the manifest. */
75
+ export function entryPath(channelId, entry) {
76
+ if (entry.state !== 'saved' || entry.format === undefined)
77
+ return undefined;
78
+ return videoThumbnailPath(channelId, entry.tab, entry.videoId, entry.format);
79
+ }
80
+ export function channelImageFile(channelId, image) {
81
+ if (image.state !== 'saved' || image.format === undefined)
82
+ return undefined;
83
+ return channelImagePath(channelId, image.kind, image.format);
84
+ }
85
+ /** True when the file a saved record describes is on disk at the recorded size. */
86
+ export async function isIntact(path, bytes) {
87
+ if (path === undefined)
88
+ return false;
89
+ try {
90
+ return (await stat(path)).size === bytes;
91
+ }
92
+ catch {
93
+ return false;
94
+ }
95
+ }
96
+ export function computeThumbnailStats(entries) {
97
+ const tabs = new Map();
98
+ let saved = 0;
99
+ let failed = 0;
100
+ let pending = 0;
101
+ let totalBytes = 0;
102
+ for (const entry of entries) {
103
+ const tab = tabs.get(entry.tab) ?? { videoCount: 0, savedCount: 0 };
104
+ tab.videoCount++;
105
+ if (entry.state === 'saved') {
106
+ saved++;
107
+ tab.savedCount++;
108
+ totalBytes += entry.bytes ?? 0;
109
+ }
110
+ else if (entry.state === 'failed')
111
+ failed++;
112
+ else
113
+ pending++;
114
+ tabs.set(entry.tab, tab);
115
+ }
116
+ return {
117
+ videoCount: entries.length,
118
+ savedCount: saved,
119
+ failedCount: failed,
120
+ pendingCount: pending,
121
+ totalBytes,
122
+ tabs: [...tabs.entries()].map(([tab, counts]) => ({ tab, ...counts })),
123
+ };
124
+ }
125
+ //# sourceMappingURL=thumbnail-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thumbnail-store.js","sourceRoot":"","sources":["../../src/utils/thumbnail-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type TranscriptSegment } from './transcript.js';
2
+ export interface TranscriptResult {
3
+ /** The whole transcript as plain text, unchanged from previous releases. */
4
+ transcript: string;
5
+ /** The same content with cue timings preserved. */
6
+ segments: TranscriptSegment[];
7
+ language: string;
8
+ videoId: string;
9
+ /** True when served from the local cache rather than refetched. */
10
+ cached: boolean;
11
+ }
12
+ export interface GetTranscriptOptions {
13
+ language?: string;
14
+ /** Ignore any cached copy and refetch from YouTube. */
15
+ refresh?: boolean;
16
+ }
17
+ /** True when a usable transcript is already on disk, in any language. */
18
+ export declare function hasCachedTranscript(videoId: string): boolean;
19
+ export declare function getTranscript(urlOrId: string, options?: GetTranscriptOptions): Promise<TranscriptResult>;
20
+ //# sourceMappingURL=transcript-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript-cache.d.ts","sourceRoot":"","sources":["../../src/utils/transcript-cache.ts"],"names":[],"mappings":"AAUA,OAAO,EAML,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AAOzB,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAqCD,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAK5D;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,gBAAgB,CAAC,CAqF3B"}