youtube-knowledge-mcp 3.0.0 → 3.0.2

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 (267) hide show
  1. package/README.md +87 -41
  2. package/dist/brain-schemas.d.ts +10 -0
  3. package/dist/brain-schemas.d.ts.map +1 -1
  4. package/dist/harvest-schemas.d.ts +6 -6
  5. package/dist/harvest-schemas.d.ts.map +1 -1
  6. package/dist/harvest-schemas.js +5 -0
  7. package/dist/harvest-schemas.js.map +1 -1
  8. package/dist/registry/brain-tools.d.ts.map +1 -1
  9. package/dist/registry/brain-tools.js +16 -0
  10. package/dist/registry/brain-tools.js.map +1 -1
  11. package/dist/registry/channel-tools.d.ts.map +1 -1
  12. package/dist/registry/channel-tools.js +16 -0
  13. package/dist/registry/channel-tools.js.map +1 -1
  14. package/dist/registry/index.d.ts.map +1 -1
  15. package/dist/registry/index.js +4 -0
  16. package/dist/registry/index.js.map +1 -1
  17. package/dist/registry/media-tools.d.ts.map +1 -1
  18. package/dist/registry/media-tools.js +31 -0
  19. package/dist/registry/media-tools.js.map +1 -1
  20. package/dist/registry/store-tools.d.ts +9 -0
  21. package/dist/registry/store-tools.d.ts.map +1 -0
  22. package/dist/registry/store-tools.js +59 -0
  23. package/dist/registry/store-tools.js.map +1 -0
  24. package/dist/registry/transcript-tools.d.ts +4 -1
  25. package/dist/registry/transcript-tools.d.ts.map +1 -1
  26. package/dist/registry/transcript-tools.js +20 -1
  27. package/dist/registry/transcript-tools.js.map +1 -1
  28. package/dist/registry/video-tools.d.ts.map +1 -1
  29. package/dist/registry/video-tools.js +48 -0
  30. package/dist/registry/video-tools.js.map +1 -1
  31. package/dist/registry/vision-tools.d.ts +7 -0
  32. package/dist/registry/vision-tools.d.ts.map +1 -0
  33. package/dist/registry/vision-tools.js +55 -0
  34. package/dist/registry/vision-tools.js.map +1 -0
  35. package/dist/schemas.d.ts +6 -0
  36. package/dist/schemas.d.ts.map +1 -1
  37. package/dist/schemas.js +2 -0
  38. package/dist/schemas.js.map +1 -1
  39. package/dist/thumbnail-schemas.d.ts +10 -0
  40. package/dist/thumbnail-schemas.d.ts.map +1 -1
  41. package/dist/tools/ask-brains.d.ts +66 -0
  42. package/dist/tools/ask-brains.d.ts.map +1 -0
  43. package/dist/tools/ask-brains.js +111 -0
  44. package/dist/tools/ask-brains.js.map +1 -0
  45. package/dist/tools/check-health.d.ts +16 -6
  46. package/dist/tools/check-health.d.ts.map +1 -1
  47. package/dist/tools/check-health.js +30 -1
  48. package/dist/tools/check-health.js.map +1 -1
  49. package/dist/tools/check-new-uploads.d.ts +55 -0
  50. package/dist/tools/check-new-uploads.d.ts.map +1 -0
  51. package/dist/tools/check-new-uploads.js +74 -0
  52. package/dist/tools/check-new-uploads.js.map +1 -0
  53. package/dist/tools/clips.d.ts +15 -0
  54. package/dist/tools/clips.d.ts.map +1 -1
  55. package/dist/tools/clips.js +5 -5
  56. package/dist/tools/clips.js.map +1 -1
  57. package/dist/tools/detect-scenes.d.ts +38 -0
  58. package/dist/tools/detect-scenes.d.ts.map +1 -0
  59. package/dist/tools/detect-scenes.js +66 -0
  60. package/dist/tools/detect-scenes.js.map +1 -0
  61. package/dist/tools/export-data.d.ts +85 -0
  62. package/dist/tools/export-data.d.ts.map +1 -0
  63. package/dist/tools/export-data.js +99 -0
  64. package/dist/tools/export-data.js.map +1 -0
  65. package/dist/tools/frames-subtitles.d.ts.map +1 -1
  66. package/dist/tools/frames-subtitles.js +16 -8
  67. package/dist/tools/frames-subtitles.js.map +1 -1
  68. package/dist/tools/frames.d.ts +73 -0
  69. package/dist/tools/frames.d.ts.map +1 -0
  70. package/dist/tools/frames.js +136 -0
  71. package/dist/tools/frames.js.map +1 -0
  72. package/dist/tools/get-channel-info.d.ts +2 -0
  73. package/dist/tools/get-channel-info.d.ts.map +1 -1
  74. package/dist/tools/get-channel-info.js +4 -0
  75. package/dist/tools/get-channel-info.js.map +1 -1
  76. package/dist/tools/get-comments.d.ts +5 -5
  77. package/dist/tools/get-coverage.d.ts +5 -5
  78. package/dist/tools/get-sponsor-segments.d.ts +49 -0
  79. package/dist/tools/get-sponsor-segments.d.ts.map +1 -0
  80. package/dist/tools/get-sponsor-segments.js +61 -0
  81. package/dist/tools/get-sponsor-segments.js.map +1 -0
  82. package/dist/tools/get-thumbnail.d.ts.map +1 -1
  83. package/dist/tools/get-thumbnail.js +2 -1
  84. package/dist/tools/get-thumbnail.js.map +1 -1
  85. package/dist/tools/get-transcript.d.ts +1 -0
  86. package/dist/tools/get-transcript.d.ts.map +1 -1
  87. package/dist/tools/get-transcript.js +11 -4
  88. package/dist/tools/get-transcript.js.map +1 -1
  89. package/dist/tools/get-video-details.d.ts +118 -0
  90. package/dist/tools/get-video-details.d.ts.map +1 -0
  91. package/dist/tools/get-video-details.js +147 -0
  92. package/dist/tools/get-video-details.js.map +1 -0
  93. package/dist/tools/gif-concat.d.ts +49 -0
  94. package/dist/tools/gif-concat.d.ts.map +1 -0
  95. package/dist/tools/gif-concat.js +96 -0
  96. package/dist/tools/gif-concat.js.map +1 -0
  97. package/dist/tools/harvest-channel.d.ts +5 -5
  98. package/dist/tools/harvest-comments.d.ts +5 -5
  99. package/dist/tools/harvest-details.d.ts +84 -0
  100. package/dist/tools/harvest-details.d.ts.map +1 -0
  101. package/dist/tools/harvest-details.js +71 -0
  102. package/dist/tools/harvest-details.js.map +1 -0
  103. package/dist/tools/list-captions.d.ts +17 -0
  104. package/dist/tools/list-captions.d.ts.map +1 -0
  105. package/dist/tools/list-captions.js +60 -0
  106. package/dist/tools/list-captions.js.map +1 -0
  107. package/dist/tools/list-channel-playlists.d.ts +30 -0
  108. package/dist/tools/list-channel-playlists.d.ts.map +1 -0
  109. package/dist/tools/list-channel-playlists.js +44 -0
  110. package/dist/tools/list-channel-playlists.js.map +1 -0
  111. package/dist/tools/query-comments.d.ts +5 -5
  112. package/dist/tools/query-videos.d.ts +5 -5
  113. package/dist/tools/search-channels.d.ts +6 -0
  114. package/dist/tools/search-channels.d.ts.map +1 -1
  115. package/dist/tools/transcribe-video.d.ts +28 -0
  116. package/dist/tools/transcribe-video.d.ts.map +1 -0
  117. package/dist/tools/transcribe-video.js +52 -0
  118. package/dist/tools/transcribe-video.js.map +1 -0
  119. package/dist/utils/brain-fusion.d.ts +24 -0
  120. package/dist/utils/brain-fusion.d.ts.map +1 -0
  121. package/dist/utils/brain-fusion.js +18 -0
  122. package/dist/utils/brain-fusion.js.map +1 -0
  123. package/dist/utils/channel-playlists.d.ts +21 -0
  124. package/dist/utils/channel-playlists.d.ts.map +1 -0
  125. package/dist/utils/channel-playlists.js +56 -0
  126. package/dist/utils/channel-playlists.js.map +1 -0
  127. package/dist/utils/clips.d.ts.map +1 -1
  128. package/dist/utils/clips.js +3 -17
  129. package/dist/utils/clips.js.map +1 -1
  130. package/dist/utils/concat.d.ts +19 -0
  131. package/dist/utils/concat.d.ts.map +1 -0
  132. package/dist/utils/concat.js +135 -0
  133. package/dist/utils/concat.js.map +1 -0
  134. package/dist/utils/contact-sheet.d.ts +28 -0
  135. package/dist/utils/contact-sheet.d.ts.map +1 -0
  136. package/dist/utils/contact-sheet.js +65 -0
  137. package/dist/utils/contact-sheet.js.map +1 -0
  138. package/dist/utils/detail-batch.d.ts +25 -0
  139. package/dist/utils/detail-batch.d.ts.map +1 -0
  140. package/dist/utils/detail-batch.js +58 -0
  141. package/dist/utils/detail-batch.js.map +1 -0
  142. package/dist/utils/errors.d.ts +1 -1
  143. package/dist/utils/errors.d.ts.map +1 -1
  144. package/dist/utils/errors.js +10 -0
  145. package/dist/utils/errors.js.map +1 -1
  146. package/dist/utils/export-data.d.ts +41 -0
  147. package/dist/utils/export-data.d.ts.map +1 -0
  148. package/dist/utils/export-data.js +114 -0
  149. package/dist/utils/export-data.js.map +1 -0
  150. package/dist/utils/export-format.d.ts +23 -0
  151. package/dist/utils/export-format.d.ts.map +1 -0
  152. package/dist/utils/export-format.js +59 -0
  153. package/dist/utils/export-format.js.map +1 -0
  154. package/dist/utils/export-load.d.ts +28 -0
  155. package/dist/utils/export-load.d.ts.map +1 -0
  156. package/dist/utils/export-load.js +70 -0
  157. package/dist/utils/export-load.js.map +1 -0
  158. package/dist/utils/export-rows.d.ts +35 -0
  159. package/dist/utils/export-rows.d.ts.map +1 -0
  160. package/dist/utils/export-rows.js +69 -0
  161. package/dist/utils/export-rows.js.map +1 -0
  162. package/dist/utils/ffmpeg.d.ts +26 -0
  163. package/dist/utils/ffmpeg.d.ts.map +1 -0
  164. package/dist/utils/ffmpeg.js +28 -0
  165. package/dist/utils/ffmpeg.js.map +1 -0
  166. package/dist/utils/format.d.ts +6 -0
  167. package/dist/utils/format.d.ts.map +1 -1
  168. package/dist/utils/format.js +4 -0
  169. package/dist/utils/format.js.map +1 -1
  170. package/dist/utils/frame-plan.d.ts +24 -0
  171. package/dist/utils/frame-plan.d.ts.map +1 -0
  172. package/dist/utils/frame-plan.js +58 -0
  173. package/dist/utils/frame-plan.js.map +1 -0
  174. package/dist/utils/frames.d.ts +32 -0
  175. package/dist/utils/frames.d.ts.map +1 -0
  176. package/dist/utils/frames.js +60 -0
  177. package/dist/utils/frames.js.map +1 -0
  178. package/dist/utils/gif.d.ts +31 -0
  179. package/dist/utils/gif.d.ts.map +1 -0
  180. package/dist/utils/gif.js +77 -0
  181. package/dist/utils/gif.js.map +1 -0
  182. package/dist/utils/harvest-catalog.d.ts +10 -0
  183. package/dist/utils/harvest-catalog.d.ts.map +1 -1
  184. package/dist/utils/harvest-catalog.js +14 -5
  185. package/dist/utils/harvest-catalog.js.map +1 -1
  186. package/dist/utils/harvest-details.d.ts +30 -0
  187. package/dist/utils/harvest-details.d.ts.map +1 -0
  188. package/dist/utils/harvest-details.js +98 -0
  189. package/dist/utils/harvest-details.js.map +1 -0
  190. package/dist/utils/local-transcription.d.ts +14 -0
  191. package/dist/utils/local-transcription.d.ts.map +1 -0
  192. package/dist/utils/local-transcription.js +85 -0
  193. package/dist/utils/local-transcription.js.map +1 -0
  194. package/dist/utils/moment.d.ts +24 -0
  195. package/dist/utils/moment.d.ts.map +1 -0
  196. package/dist/utils/moment.js +22 -0
  197. package/dist/utils/moment.js.map +1 -0
  198. package/dist/utils/most-replayed.d.ts +15 -0
  199. package/dist/utils/most-replayed.d.ts.map +1 -0
  200. package/dist/utils/most-replayed.js +25 -0
  201. package/dist/utils/most-replayed.js.map +1 -0
  202. package/dist/utils/scenes.d.ts +39 -0
  203. package/dist/utils/scenes.d.ts.map +1 -0
  204. package/dist/utils/scenes.js +83 -0
  205. package/dist/utils/scenes.js.map +1 -0
  206. package/dist/utils/seconds.d.ts +5 -0
  207. package/dist/utils/seconds.d.ts.map +1 -0
  208. package/dist/utils/seconds.js +7 -0
  209. package/dist/utils/seconds.js.map +1 -0
  210. package/dist/utils/sponsorblock.d.ts +24 -0
  211. package/dist/utils/sponsorblock.d.ts.map +1 -0
  212. package/dist/utils/sponsorblock.js +153 -0
  213. package/dist/utils/sponsorblock.js.map +1 -0
  214. package/dist/utils/transcript-cache.d.ts +5 -1
  215. package/dist/utils/transcript-cache.d.ts.map +1 -1
  216. package/dist/utils/transcript-cache.js +31 -50
  217. package/dist/utils/transcript-cache.js.map +1 -1
  218. package/dist/utils/transcript-files.d.ts +24 -0
  219. package/dist/utils/transcript-files.d.ts.map +1 -0
  220. package/dist/utils/transcript-files.js +61 -0
  221. package/dist/utils/transcript-files.js.map +1 -0
  222. package/dist/utils/transcript.d.ts +3 -0
  223. package/dist/utils/transcript.d.ts.map +1 -1
  224. package/dist/utils/transcript.js +2 -0
  225. package/dist/utils/transcript.js.map +1 -1
  226. package/dist/utils/upload-check.d.ts +38 -0
  227. package/dist/utils/upload-check.d.ts.map +1 -0
  228. package/dist/utils/upload-check.js +93 -0
  229. package/dist/utils/upload-check.js.map +1 -0
  230. package/dist/utils/validate.d.ts +2 -0
  231. package/dist/utils/validate.d.ts.map +1 -1
  232. package/dist/utils/validate.js +4 -0
  233. package/dist/utils/validate.js.map +1 -1
  234. package/dist/utils/video-detail-store.d.ts +47 -0
  235. package/dist/utils/video-detail-store.d.ts.map +1 -0
  236. package/dist/utils/video-detail-store.js +89 -0
  237. package/dist/utils/video-detail-store.js.map +1 -0
  238. package/dist/utils/video-stats-cache.js +1 -1
  239. package/dist/utils/video-stats-schema.d.ts +187 -0
  240. package/dist/utils/video-stats-schema.d.ts.map +1 -0
  241. package/dist/utils/video-stats-schema.js +50 -0
  242. package/dist/utils/video-stats-schema.js.map +1 -0
  243. package/dist/utils/video-stats-storage.d.ts +16 -0
  244. package/dist/utils/video-stats-storage.d.ts.map +1 -0
  245. package/dist/utils/video-stats-storage.js +37 -0
  246. package/dist/utils/video-stats-storage.js.map +1 -0
  247. package/dist/utils/video-stats.d.ts +23 -127
  248. package/dist/utils/video-stats.d.ts.map +1 -1
  249. package/dist/utils/video-stats.js +42 -102
  250. package/dist/utils/video-stats.js.map +1 -1
  251. package/dist/utils/video-stream.d.ts +21 -0
  252. package/dist/utils/video-stream.d.ts.map +1 -0
  253. package/dist/utils/video-stream.js +36 -0
  254. package/dist/utils/video-stream.js.map +1 -0
  255. package/dist/utils/whisper.d.ts +39 -0
  256. package/dist/utils/whisper.d.ts.map +1 -0
  257. package/dist/utils/whisper.js +137 -0
  258. package/dist/utils/whisper.js.map +1 -0
  259. package/dist/utils/youtube-channel.d.ts +4 -0
  260. package/dist/utils/youtube-channel.d.ts.map +1 -1
  261. package/dist/utils/youtube-channel.js +6 -0
  262. package/dist/utils/youtube-channel.js.map +1 -1
  263. package/dist/utils/youtube-schemas.d.ts +6 -0
  264. package/dist/utils/youtube-schemas.d.ts.map +1 -1
  265. package/dist/utils/youtube-schemas.js +2 -0
  266. package/dist/utils/youtube-schemas.js.map +1 -1
  267. package/package.json +9 -3
@@ -20,5 +20,7 @@ export declare function resolveOutputDir(outputDir: string | undefined, fallback
20
20
  export declare function assertLanguageTag(language: string): string;
21
21
  /** Accepts `90`, `1:30`, `01:02:03`, `1:30.5` and returns seconds. */
22
22
  export declare function parseTimestamp(value: string | number, field: string): number;
23
+ /** `parseTimestamp` for a field the caller may leave out. */
24
+ export declare function parseOptionalTimestamp(value: string | number | undefined, field: string): number | undefined;
23
25
  export * from './validate-youtube.js';
24
26
  //# sourceMappingURL=validate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAqBpE;AAkBD,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxF;AAKD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO1D;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAoB5E;AAED,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAqBpE;AAkBD,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxF;AAKD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO1D;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAoB5E;AAED,6DAA6D;AAC7D,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,EAClC,KAAK,EAAE,MAAM,GACZ,MAAM,GAAG,SAAS,CAEpB;AAED,cAAc,uBAAuB,CAAC"}
@@ -85,5 +85,9 @@ export function parseTimestamp(value, field) {
85
85
  // The hours group is optional, so it is genuinely absent for "MM:SS" input.
86
86
  return Number(match[1] ?? 0) * 3600 + Number(match[2] ?? 0) * 60 + Number(match[3] ?? 0);
87
87
  }
88
+ /** `parseTimestamp` for a field the caller may leave out. */
89
+ export function parseOptionalTimestamp(value, field) {
90
+ return value === undefined ? undefined : parseTimestamp(value, field);
91
+ }
88
92
  export * from './validate-youtube.js';
89
93
  //# sourceMappingURL=validate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/utils/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ import { z } from 'zod';
3
+ import { type videoStatsRowSchema } from './video-stats.js';
4
+ /**
5
+ * Keeping what a full read of a video said, on the catalogue row that names it.
6
+ *
7
+ * A flat listing knows a title, a duration and a view count. Reading the video itself
8
+ * adds what the listing cannot: the record, the replay heatmap, and the counts YouTube
9
+ * states, which a comment receipt is measured against. Until now those columns
10
+ * existed and nothing wrote them, so a video was never "detailed".
11
+ */
12
+ type StatsRow = z.infer<typeof videoStatsRowSchema>;
13
+ /** Writes a read onto the video's catalogue row. A video the catalogue has not seen is left alone. */
14
+ export declare function saveVideoDetail(database: DatabaseSync, row: StatsRow, now: number): void;
15
+ /**
16
+ * What a video's `availability` is set to when it could not be read, by the error it
17
+ * gave. These videos are skipped from then on: left in the queue they would be tried
18
+ * first, fail first, and starve every video behind them on every run.
19
+ */
20
+ export declare const UNREADABLE_AVAILABILITY: {
21
+ readonly PRIVATE: "private";
22
+ readonly MEMBERS_ONLY: "subscriber_only";
23
+ readonly PREMIUM_ONLY: "premium_only";
24
+ readonly NOT_FOUND: "unavailable";
25
+ readonly AGE_GATED: "needs_auth";
26
+ };
27
+ /**
28
+ * For a failure that is not one of those: a live stream not over, a timeout, an error
29
+ * yt-dlp could not explain. It may well pass later, so it is told apart from the above,
30
+ * but it is skipped all the same, or a video that keeps failing would hold the queue.
31
+ */
32
+ export declare const READ_FAILED_AVAILABILITY = "read_failed";
33
+ export type UnreadableAvailability = (typeof UNREADABLE_AVAILABILITY)[keyof typeof UNREADABLE_AVAILABILITY] | typeof READ_FAILED_AVAILABILITY;
34
+ /** Remembers that this video cannot be read, without pretending it was. */
35
+ export declare function markUnreadable(database: DatabaseSync, videoId: string, availability: UnreadableAvailability): void;
36
+ /** The channel's catalogued videos not yet read, in catalogue order. */
37
+ export declare function pendingVideoIds(database: DatabaseSync, channelId: string, limit: number, options?: {
38
+ includeUnreadable?: boolean;
39
+ }): string[];
40
+ /** How many of the channel's videos are known to be unreadable, and so never queued. */
41
+ export declare function unreadableCount(database: DatabaseSync, channelId: string): number;
42
+ export declare function catalogCounts(database: DatabaseSync, channelId: string): {
43
+ videos: number;
44
+ detailed: number;
45
+ };
46
+ export {};
47
+ //# sourceMappingURL=video-detail-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-detail-store.d.ts","sourceRoot":"","sources":["../../src/utils/video-detail-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAA0C,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEpG;;;;;;;GAOG;AAEH,KAAK,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAmCpD,sGAAsG;AACtG,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAmBxF;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;;;;;CAM1B,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,gBAAgB,CAAC;AAEtD,MAAM,MAAM,sBAAsB,GAC9B,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,OAAO,uBAAuB,CAAC,GACtE,OAAO,wBAAwB,CAAC;AAQpC,2EAA2E;AAC3E,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,sBAAsB,GACnC,IAAI,CAIN;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC5C,MAAM,EAAE,CAgBV;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAUjF;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,MAAM,GAChB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAStC"}
@@ -0,0 +1,89 @@
1
+ import { z } from 'zod';
2
+ import { queryRow, queryRows } from './store-rows.js';
3
+ import { MAX_DETAIL_JSON_BYTES, stripForStorage } from './video-stats.js';
4
+ /** Parts of a record, bulkiest first, given up before a record is cut down to its identity. */
5
+ const DROPPED_WHEN_TOO_LARGE = ['description', 'chapters', 'tags', 'categories'];
6
+ const UPDATE = `
7
+ UPDATE video SET
8
+ duration_s = COALESCE(?, duration_s),
9
+ upload_date = COALESCE(?, upload_date),
10
+ view_count = COALESCE(?, view_count),
11
+ like_count = COALESCE(?, like_count),
12
+ comment_count = COALESCE(?, comment_count),
13
+ live_status = COALESCE(?, live_status),
14
+ availability = COALESCE(?, availability),
15
+ thumbnail_url = COALESCE(?, thumbnail_url),
16
+ detail_at = ?,
17
+ detail_json = ?,
18
+ heatmap_json = ?
19
+ WHERE video_id = ?`;
20
+ /** The record as JSON that fits the cap, losing the least that is needed to fit. */
21
+ function recordJson(row) {
22
+ const record = stripForStorage({ ...row });
23
+ let json = JSON.stringify(record);
24
+ if (Buffer.byteLength(json) <= MAX_DETAIL_JSON_BYTES)
25
+ return json;
26
+ const trimmed = { ...record, detail_truncated: true };
27
+ for (const key of DROPPED_WHEN_TOO_LARGE) {
28
+ Reflect.deleteProperty(trimmed, key);
29
+ json = JSON.stringify(trimmed);
30
+ if (Buffer.byteLength(json) <= MAX_DETAIL_JSON_BYTES)
31
+ return json;
32
+ }
33
+ return JSON.stringify({ id: row.id, title: row.title ?? null, detail_truncated: true });
34
+ }
35
+ /** Writes a read onto the video's catalogue row. A video the catalogue has not seen is left alone. */
36
+ export function saveVideoDetail(database, row, now) {
37
+ const heatmap = row.heatmap ?? [];
38
+ database
39
+ .prepare(UPDATE)
40
+ .run(row.duration === null || row.duration === undefined ? null : Math.round(row.duration), row.upload_date ?? null, row.view_count ?? null, row.like_count ?? null, row.comment_count ?? null, row.live_status ?? null, row.availability ?? null, row.thumbnail ?? null, now, recordJson(row), heatmap.length === 0 ? null : JSON.stringify(heatmap), row.id);
41
+ }
42
+ /**
43
+ * What a video's `availability` is set to when it could not be read, by the error it
44
+ * gave. These videos are skipped from then on: left in the queue they would be tried
45
+ * first, fail first, and starve every video behind them on every run.
46
+ */
47
+ export const UNREADABLE_AVAILABILITY = {
48
+ PRIVATE: 'private',
49
+ MEMBERS_ONLY: 'subscriber_only',
50
+ PREMIUM_ONLY: 'premium_only',
51
+ NOT_FOUND: 'unavailable',
52
+ AGE_GATED: 'needs_auth',
53
+ };
54
+ /**
55
+ * For a failure that is not one of those: a live stream not over, a timeout, an error
56
+ * yt-dlp could not explain. It may well pass later, so it is told apart from the above,
57
+ * but it is skipped all the same, or a video that keeps failing would hold the queue.
58
+ */
59
+ export const READ_FAILED_AVAILABILITY = 'read_failed';
60
+ const UNREADABLE_VALUES = [
61
+ ...Object.values(UNREADABLE_AVAILABILITY),
62
+ READ_FAILED_AVAILABILITY,
63
+ ];
64
+ const NOT_UNREADABLE = `(availability IS NULL OR availability NOT IN (${UNREADABLE_VALUES.map(() => '?').join(', ')}))`;
65
+ /** Remembers that this video cannot be read, without pretending it was. */
66
+ export function markUnreadable(database, videoId, availability) {
67
+ database
68
+ .prepare('UPDATE video SET availability = ? WHERE video_id = ?')
69
+ .run(availability, videoId);
70
+ }
71
+ /** The channel's catalogued videos not yet read, in catalogue order. */
72
+ export function pendingVideoIds(database, channelId, limit, options = {}) {
73
+ const unreadable = options.includeUnreadable === true ? '' : ` AND ${NOT_UNREADABLE}`;
74
+ const params = options.includeUnreadable === true ? [] : UNREADABLE_VALUES;
75
+ return queryRows(database.prepare(`SELECT video_id FROM video
76
+ WHERE channel_id = ? AND detail_at IS NULL${unreadable}
77
+ ORDER BY catalog_rank ASC NULLS LAST, video_id
78
+ LIMIT ?`), z.object({ video_id: z.string() }), channelId, ...params, limit).map((row) => row.video_id);
79
+ }
80
+ /** How many of the channel's videos are known to be unreadable, and so never queued. */
81
+ export function unreadableCount(database, channelId) {
82
+ const row = queryRow(database.prepare(`SELECT COUNT(*) AS n FROM video WHERE channel_id = ? AND detail_at IS NULL AND NOT ${NOT_UNREADABLE}`), z.object({ n: z.number() }), channelId, ...UNREADABLE_VALUES);
83
+ return row?.n ?? 0;
84
+ }
85
+ export function catalogCounts(database, channelId) {
86
+ const row = queryRow(database.prepare('SELECT COUNT(*) AS videos, COUNT(detail_at) AS detailed FROM video WHERE channel_id = ?'), z.object({ videos: z.number(), detailed: z.number() }), channelId);
87
+ return { videos: row?.videos ?? 0, detailed: row?.detailed ?? 0 };
88
+ }
89
+ //# sourceMappingURL=video-detail-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-detail-store.js","sourceRoot":"","sources":["../../src/utils/video-detail-store.ts"],"names":[],"mappings":"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"}
@@ -35,7 +35,7 @@ function cacheDir() {
35
35
  /** Counts move constantly, so a day is already generous. */
36
36
  const VIDEO_STATS_TTL_MS = envInt('YOUTUBE_MCP_STATS_TTL_MS', 86_400_000, { min: 0 });
37
37
  /** Bumped when the stored shape changes, so old files are refetched, not misread. */
38
- const CACHE_VERSION = 1;
38
+ const CACHE_VERSION = 2;
39
39
  const cachedStatsSchema = z.object({
40
40
  version: z.literal(CACHE_VERSION),
41
41
  fetchedAt: z.number(),
@@ -0,0 +1,187 @@
1
+ import { z } from 'zod';
2
+ /** The shape of one `yt-dlp -j` read of a video, and the small records derived from it. */
3
+ export declare const heatmapEntrySchema: z.ZodObject<{
4
+ start_time: z.ZodNumber;
5
+ end_time: z.ZodNumber;
6
+ value: z.ZodNumber;
7
+ }, "strip", z.ZodTypeAny, {
8
+ value: number;
9
+ start_time: number;
10
+ end_time: number;
11
+ }, {
12
+ value: number;
13
+ start_time: number;
14
+ end_time: number;
15
+ }>;
16
+ export declare const videoStatsRowSchema: z.ZodObject<{
17
+ id: z.ZodString;
18
+ title: z.ZodOptional<z.ZodNullable<z.ZodString>>;
19
+ channel: z.ZodOptional<z.ZodNullable<z.ZodString>>;
20
+ channel_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
21
+ channel_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
22
+ channel_follower_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
23
+ upload_date: z.ZodOptional<z.ZodNullable<z.ZodString>>;
24
+ duration: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
25
+ view_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
26
+ like_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
27
+ comment_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
28
+ concurrent_view_count: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
29
+ categories: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
30
+ tags: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
31
+ age_limit: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
32
+ availability: z.ZodOptional<z.ZodNullable<z.ZodString>>;
33
+ live_status: z.ZodOptional<z.ZodNullable<z.ZodString>>;
34
+ was_live: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
35
+ is_live: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
36
+ description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
37
+ thumbnail: z.ZodOptional<z.ZodNullable<z.ZodString>>;
38
+ chapters: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
39
+ title: z.ZodString;
40
+ start_time: z.ZodNumber;
41
+ end_time: z.ZodNumber;
42
+ }, "strip", z.ZodTypeAny, {
43
+ title: string;
44
+ start_time: number;
45
+ end_time: number;
46
+ }, {
47
+ title: string;
48
+ start_time: number;
49
+ end_time: number;
50
+ }>, "many">>>;
51
+ heatmap: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodObject<{
52
+ start_time: z.ZodNumber;
53
+ end_time: z.ZodNumber;
54
+ value: z.ZodNumber;
55
+ }, "strip", z.ZodTypeAny, {
56
+ value: number;
57
+ start_time: number;
58
+ end_time: number;
59
+ }, {
60
+ value: number;
61
+ start_time: number;
62
+ end_time: number;
63
+ }>, "many">>>;
64
+ automatic_captions: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
65
+ subtitles: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
66
+ uploader: z.ZodOptional<z.ZodNullable<z.ZodString>>;
67
+ license: z.ZodOptional<z.ZodNullable<z.ZodString>>;
68
+ language: z.ZodOptional<z.ZodNullable<z.ZodString>>;
69
+ media_type: z.ZodOptional<z.ZodNullable<z.ZodString>>;
70
+ playable_in_embed: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
71
+ release_timestamp: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
72
+ track: z.ZodOptional<z.ZodNullable<z.ZodString>>;
73
+ artist: z.ZodOptional<z.ZodNullable<z.ZodString>>;
74
+ album: z.ZodOptional<z.ZodNullable<z.ZodString>>;
75
+ release_year: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
76
+ width: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
77
+ height: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
78
+ fps: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
79
+ }, "strip", z.ZodTypeAny, {
80
+ id: string;
81
+ title?: string | null | undefined;
82
+ channel?: string | null | undefined;
83
+ description?: string | null | undefined;
84
+ tags?: string[] | null | undefined;
85
+ fps?: number | null | undefined;
86
+ language?: string | null | undefined;
87
+ duration?: number | null | undefined;
88
+ upload_date?: string | null | undefined;
89
+ thumbnail?: string | null | undefined;
90
+ view_count?: number | null | undefined;
91
+ like_count?: number | null | undefined;
92
+ comment_count?: number | null | undefined;
93
+ availability?: string | null | undefined;
94
+ live_status?: string | null | undefined;
95
+ chapters?: {
96
+ title: string;
97
+ start_time: number;
98
+ end_time: number;
99
+ }[] | null | undefined;
100
+ width?: number | null | undefined;
101
+ height?: number | null | undefined;
102
+ subtitles?: Record<string, unknown> | null | undefined;
103
+ automatic_captions?: Record<string, unknown> | null | undefined;
104
+ channel_id?: string | null | undefined;
105
+ channel_url?: string | null | undefined;
106
+ channel_follower_count?: number | null | undefined;
107
+ concurrent_view_count?: number | null | undefined;
108
+ categories?: string[] | null | undefined;
109
+ age_limit?: number | null | undefined;
110
+ was_live?: boolean | null | undefined;
111
+ is_live?: boolean | null | undefined;
112
+ heatmap?: {
113
+ value: number;
114
+ start_time: number;
115
+ end_time: number;
116
+ }[] | null | undefined;
117
+ uploader?: string | null | undefined;
118
+ license?: string | null | undefined;
119
+ media_type?: string | null | undefined;
120
+ playable_in_embed?: boolean | null | undefined;
121
+ release_timestamp?: number | null | undefined;
122
+ track?: string | null | undefined;
123
+ artist?: string | null | undefined;
124
+ album?: string | null | undefined;
125
+ release_year?: number | null | undefined;
126
+ }, {
127
+ id: string;
128
+ title?: string | null | undefined;
129
+ channel?: string | null | undefined;
130
+ description?: string | null | undefined;
131
+ tags?: string[] | null | undefined;
132
+ fps?: number | null | undefined;
133
+ language?: string | null | undefined;
134
+ duration?: number | null | undefined;
135
+ upload_date?: string | null | undefined;
136
+ thumbnail?: string | null | undefined;
137
+ view_count?: number | null | undefined;
138
+ like_count?: number | null | undefined;
139
+ comment_count?: number | null | undefined;
140
+ availability?: string | null | undefined;
141
+ live_status?: string | null | undefined;
142
+ chapters?: {
143
+ title: string;
144
+ start_time: number;
145
+ end_time: number;
146
+ }[] | null | undefined;
147
+ width?: number | null | undefined;
148
+ height?: number | null | undefined;
149
+ subtitles?: Record<string, unknown> | null | undefined;
150
+ automatic_captions?: Record<string, unknown> | null | undefined;
151
+ channel_id?: string | null | undefined;
152
+ channel_url?: string | null | undefined;
153
+ channel_follower_count?: number | null | undefined;
154
+ concurrent_view_count?: number | null | undefined;
155
+ categories?: string[] | null | undefined;
156
+ age_limit?: number | null | undefined;
157
+ was_live?: boolean | null | undefined;
158
+ is_live?: boolean | null | undefined;
159
+ heatmap?: {
160
+ value: number;
161
+ start_time: number;
162
+ end_time: number;
163
+ }[] | null | undefined;
164
+ uploader?: string | null | undefined;
165
+ license?: string | null | undefined;
166
+ media_type?: string | null | undefined;
167
+ playable_in_embed?: boolean | null | undefined;
168
+ release_timestamp?: number | null | undefined;
169
+ track?: string | null | undefined;
170
+ artist?: string | null | undefined;
171
+ album?: string | null | undefined;
172
+ release_year?: number | null | undefined;
173
+ }>;
174
+ /** What YouTube credits a track with, when a video carries music metadata. */
175
+ export interface MusicCredits {
176
+ track?: string;
177
+ artist?: string;
178
+ album?: string;
179
+ releaseYear?: number;
180
+ }
181
+ /** The frame size and rate of the best stream, as far as yt-dlp reported them. */
182
+ export interface VideoFormat {
183
+ width?: number;
184
+ height?: number;
185
+ fps?: number;
186
+ }
187
+ //# sourceMappingURL=video-stats-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-stats-schema.d.ts","sourceRoot":"","sources":["../../src/utils/video-stats-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,2FAA2F;AAE3F,eAAO,MAAM,kBAAkB;;;;;;;;;;;;EAI7B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyC9B,CAAC;AAEH,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd"}
@@ -0,0 +1,50 @@
1
+ import { z } from 'zod';
2
+ /** The shape of one `yt-dlp -j` read of a video, and the small records derived from it. */
3
+ export const heatmapEntrySchema = z.object({
4
+ start_time: z.number(),
5
+ end_time: z.number(),
6
+ value: z.number(),
7
+ });
8
+ export const videoStatsRowSchema = z.object({
9
+ id: z.string(),
10
+ title: z.string().nullish(),
11
+ channel: z.string().nullish(),
12
+ channel_id: z.string().nullish(),
13
+ channel_url: z.string().nullish(),
14
+ channel_follower_count: z.number().nullish(),
15
+ upload_date: z.string().nullish(),
16
+ duration: z.number().nullish(),
17
+ view_count: z.number().nullish(),
18
+ like_count: z.number().nullish(),
19
+ comment_count: z.number().nullish(),
20
+ concurrent_view_count: z.number().nullish(),
21
+ categories: z.array(z.string()).nullish(),
22
+ tags: z.array(z.string()).nullish(),
23
+ age_limit: z.number().nullish(),
24
+ availability: z.string().nullish(),
25
+ live_status: z.string().nullish(),
26
+ was_live: z.boolean().nullish(),
27
+ is_live: z.boolean().nullish(),
28
+ description: z.string().nullish(),
29
+ thumbnail: z.string().nullish(),
30
+ chapters: z
31
+ .array(z.object({ title: z.string(), start_time: z.number(), end_time: z.number() }))
32
+ .nullish(),
33
+ heatmap: z.array(heatmapEntrySchema).nullish(),
34
+ automatic_captions: z.record(z.string(), z.unknown()).nullish(),
35
+ subtitles: z.record(z.string(), z.unknown()).nullish(),
36
+ uploader: z.string().nullish(),
37
+ license: z.string().nullish(),
38
+ language: z.string().nullish(),
39
+ media_type: z.string().nullish(),
40
+ playable_in_embed: z.boolean().nullish(),
41
+ release_timestamp: z.number().nullish(),
42
+ track: z.string().nullish(),
43
+ artist: z.string().nullish(),
44
+ album: z.string().nullish(),
45
+ release_year: z.number().nullish(),
46
+ width: z.number().nullish(),
47
+ height: z.number().nullish(),
48
+ fps: z.number().nullish(),
49
+ });
50
+ //# sourceMappingURL=video-stats-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-stats-schema.js","sourceRoot":"","sources":["../../src/utils/video-stats-schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * What of a video's record is kept in the store.
3
+ *
4
+ * Measured on a 637KB payload: `automatic_captions` is 460,965 bytes (72%) of URLs that
5
+ * expire, `formats` another 113,601 (18%), and `heatmap` 6,589 — larger than the whole
6
+ * rest of the record, so it has its own column. Stripped, a video is 4,785 bytes. Storing
7
+ * the raw object would cost 6.4GB per 10,000 videos instead of 48MB.
8
+ */
9
+ /** A stored snapshot must stay small enough that a catalogue is not mostly URLs. */
10
+ export declare const MAX_DETAIL_JSON_BYTES = 32768;
11
+ /** Keys dropped before storage: expiring URLs and bulk we can re-fetch. */
12
+ export declare const STRIPPED_KEYS: readonly ["formats", "requested_formats", "automatic_captions", "subtitles", "thumbnails", "comments", "heatmap"];
13
+ export declare const CATALOG_DESCRIPTION_PREVIEW_CHARS = 500;
14
+ /** Drops the bulk keys, so what is stored is the record rather than its attachments. */
15
+ export declare function stripForStorage(raw: Record<string, unknown>): Record<string, unknown>;
16
+ //# sourceMappingURL=video-stats-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-stats-storage.d.ts","sourceRoot":"","sources":["../../src/utils/video-stats-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAE5C,2EAA2E;AAC3E,eAAO,MAAM,aAAa,mHAQhB,CAAC;AAEX,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAcrF"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * What of a video's record is kept in the store.
3
+ *
4
+ * Measured on a 637KB payload: `automatic_captions` is 460,965 bytes (72%) of URLs that
5
+ * expire, `formats` another 113,601 (18%), and `heatmap` 6,589 — larger than the whole
6
+ * rest of the record, so it has its own column. Stripped, a video is 4,785 bytes. Storing
7
+ * the raw object would cost 6.4GB per 10,000 videos instead of 48MB.
8
+ */
9
+ /** A stored snapshot must stay small enough that a catalogue is not mostly URLs. */
10
+ export const MAX_DETAIL_JSON_BYTES = 32_768;
11
+ /** Keys dropped before storage: expiring URLs and bulk we can re-fetch. */
12
+ export const STRIPPED_KEYS = [
13
+ 'formats',
14
+ 'requested_formats',
15
+ 'automatic_captions',
16
+ 'subtitles',
17
+ 'thumbnails',
18
+ 'comments',
19
+ 'heatmap',
20
+ ];
21
+ export const CATALOG_DESCRIPTION_PREVIEW_CHARS = 500;
22
+ /** Drops the bulk keys, so what is stored is the record rather than its attachments. */
23
+ export function stripForStorage(raw) {
24
+ const kept = {};
25
+ for (const [key, value] of Object.entries(raw)) {
26
+ if (STRIPPED_KEYS.some((stripped) => stripped === key))
27
+ continue;
28
+ kept[key] = value;
29
+ }
30
+ const description = kept.description;
31
+ if (typeof description === 'string' && description.length > CATALOG_DESCRIPTION_PREVIEW_CHARS) {
32
+ kept.description = description.slice(0, CATALOG_DESCRIPTION_PREVIEW_CHARS);
33
+ kept.description_truncated = true;
34
+ }
35
+ return kept;
36
+ }
37
+ //# sourceMappingURL=video-stats-storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-stats-storage.js","sourceRoot":"","sources":["../../src/utils/video-stats-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oFAAoF;AACpF,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAE5C,2EAA2E;AAC3E,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,SAAS;IACT,mBAAmB;IACnB,oBAAoB;IACpB,WAAW;IACX,YAAY;IACZ,UAAU;IACV,SAAS;CACD,CAAC;AAEX,MAAM,CAAC,MAAM,iCAAiC,GAAG,GAAG,CAAC;AAErD,wFAAwF;AACxF,MAAM,UAAU,eAAe,CAAC,GAA4B;IAC1D,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,KAAK,GAAG,CAAC;YAAE,SAAS;QACjE,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IACrC,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,CAAC,MAAM,GAAG,iCAAiC,EAAE,CAAC;QAC9F,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,iCAAiC,CAAC,CAAC;QAC3E,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC;IACpC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}