luckiest-co 1.0.32 → 1.0.34

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/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/commands/plan.md +1 -0
  4. package/package.json +1 -1
  5. package/skills/luckiest-plan/SKILL.md +1 -0
  6. package/skills/luckiest-research/CHANGELOG.md +5 -0
  7. package/skills/luckiest-research/SKILL.md +8 -4
  8. package/skills/luckiest-video-studio/ATTRIBUTION.md +11 -0
  9. package/skills/luckiest-video-studio/CHANGELOG.md +56 -0
  10. package/skills/luckiest-video-studio/SKILL.md +35 -7
  11. package/skills/luckiest-video-studio/references/audio.md +61 -10
  12. package/skills/luckiest-video-studio/references/lessons.md +44 -0
  13. package/skills/luckiest-video-studio/references/step-2-plan.md +32 -1
  14. package/skills/luckiest-video-studio/references/step-3-compose.md +28 -0
  15. package/skills/luckiest-video-studio/references/step-4-deliver.md +24 -1
  16. package/skills/luckiest-video-studio/references/storyboard.md +24 -3
  17. package/skills/luckiest-video-studio/scripts/qa.mjs +167 -5
  18. package/skills/luckiest-video-studio/scripts/qa.test.mjs +99 -1
  19. package/skills/luckiest-video-studio/scripts/render-scenes.mjs +196 -71
  20. package/skills/luckiest-video-studio/scripts/render-scenes.test.mjs +109 -0
  21. package/skills/luckiest-video-studio/scripts/sfx-cues.mjs +116 -0
  22. package/skills/luckiest-video-studio/scripts/sfx-cues.test.mjs +76 -0
  23. package/skills/luckiest-video-studio/scripts/stamp-format.mjs +26 -0
  24. package/skills/luckiest-video-studio/scripts/storyboard.schema.json +23 -0
  25. package/skills/luckiest-video-studio/templates/logo-line-draw.html +12 -5
  26. package/skills/luckiest-video-studio-art-director/ATTRIBUTION.md +1 -0
  27. package/skills/luckiest-video-studio-art-director/CHANGELOG.md +8 -0
  28. package/skills/luckiest-video-studio-art-director/SKILL.md +18 -5
  29. package/skills/luckiest-video-studio-cards/CHANGELOG.md +18 -0
  30. package/skills/luckiest-video-studio-cards/SKILL.md +22 -7
  31. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-agenda.html +17 -6
  32. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-bigtype.html +15 -5
  33. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-board.html +20 -6
  34. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-checklist.html +27 -6
  35. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-compare.html +40 -9
  36. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-deck.html +26 -6
  37. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-dossier.html +28 -9
  38. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-filmstrip.html +36 -5
  39. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-grid6.html +24 -6
  40. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-indexset.html +34 -6
  41. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-maplegend.html +39 -8
  42. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-matrix.html +35 -7
  43. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-numlist.html +26 -7
  44. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-outline.html +26 -6
  45. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-phases.html +34 -7
  46. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-quad.html +41 -10
  47. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-roadmap.html +66 -17
  48. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-stepper.html +42 -7
  49. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-toc.html +26 -8
  50. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-overview-vtimeline.html +35 -9
  51. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-bracket.html +16 -4
  52. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-circle.html +21 -5
  53. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-classified.html +25 -7
  54. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-collage.html +27 -9
  55. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-correction.html +19 -7
  56. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-crt.html +15 -4
  57. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-cutout.html +18 -8
  58. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-fullbleed.html +18 -5
  59. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-highlight.html +15 -5
  60. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-indexcard.html +18 -5
  61. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-map.html +20 -8
  62. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-markerbox.html +12 -5
  63. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-numeral.html +21 -7
  64. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-split.html +25 -9
  65. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-stacked.html +26 -10
  66. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-stamp.html +14 -5
  67. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-ticker.html +26 -9
  68. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-section-tornstrip.html +13 -4
  69. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-annotated.html +29 -9
  70. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-arc.html +25 -7
  71. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-axis.html +34 -14
  72. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-bar.html +17 -8
  73. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-bracket.html +24 -8
  74. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-calendar.html +24 -9
  75. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-callout.html +24 -5
  76. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-columns.html +27 -9
  77. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-corner.html +22 -9
  78. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-dots.html +27 -8
  79. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-engraving.html +30 -8
  80. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-footnote.html +20 -8
  81. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-frac.html +24 -8
  82. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-heat.html +22 -8
  83. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-isotype.html +29 -8
  84. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-ledger.html +18 -6
  85. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-left.html +27 -10
  86. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-magnifier.html +21 -8
  87. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-map.html +21 -9
  88. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-meter.html +26 -9
  89. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-multiplier.html +27 -12
  90. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-odo.html +18 -5
  91. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-pack.html +41 -17
  92. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-photo.html +18 -7
  93. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-pie.html +25 -8
  94. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-postage.html +24 -8
  95. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-pricetag.html +25 -11
  96. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-quote.html +21 -8
  97. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-ranking.html +25 -10
  98. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-receipt.html +24 -12
  99. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-ribbon.html +34 -12
  100. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-semi.html +22 -7
  101. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-spotlight.html +23 -9
  102. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-stamp.html +24 -7
  103. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-steps.html +30 -9
  104. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-tape.html +33 -12
  105. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-torn.html +22 -9
  106. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-trend.html +23 -9
  107. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-vs.html +30 -10
  108. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat-window.html +38 -25
  109. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-stat.html +23 -4
  110. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier1/t1-timeline.html +41 -6
  111. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-label.html +10 -3
  112. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-bracket.html +9 -2
  113. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-caption.html +12 -5
  114. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-circle.html +10 -3
  115. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-definition.html +10 -3
  116. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-footnote.html +11 -4
  117. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-magnify.html +9 -2
  118. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-pin.html +9 -2
  119. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-pullquote.html +10 -3
  120. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-spotlight.html +15 -2
  121. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-sticky.html +9 -2
  122. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-tag.html +10 -3
  123. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lb-underline.html +9 -2
  124. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lower-third.html +9 -2
  125. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-badge.html +10 -3
  126. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-barslab.html +26 -7
  127. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-bracket.html +9 -2
  128. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-clapper.html +9 -2
  129. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-highlight.html +9 -2
  130. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-indextab.html +10 -3
  131. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-rail.html +9 -2
  132. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-ribbon.html +9 -2
  133. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-stacked.html +9 -2
  134. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-stamp.html +9 -2
  135. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-sticky.html +9 -2
  136. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/cards/tier2/t2-lt-typecard.html +9 -2
  137. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/style.json +1 -1
  138. package/skills/luckiest-video-studio-cards/packs/01-paper-collage/tokens.css +18 -8
  139. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-agenda.html +23 -5
  140. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-beforeafter.html +30 -6
  141. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-bento.html +34 -8
  142. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-bigleft.html +28 -6
  143. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-callouts.html +36 -7
  144. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-cardrow5.html +17 -4
  145. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-checklist.html +11 -3
  146. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-cycle.html +30 -6
  147. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-dashboard.html +32 -5
  148. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-deck2.html +21 -5
  149. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-deck3.html +18 -4
  150. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-duallist.html +24 -4
  151. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-featurecompare.html +19 -5
  152. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-featuregrid.html +25 -4
  153. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-flashcards.html +22 -5
  154. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-flow.html +23 -4
  155. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-funnel.html +19 -5
  156. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-gantt.html +22 -6
  157. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-grid6.html +17 -4
  158. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-grid9.html +15 -4
  159. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-iconlist.html +18 -3
  160. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-journey.html +41 -6
  161. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-kpirow.html +24 -6
  162. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-layers.html +20 -5
  163. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-legend.html +19 -4
  164. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-levels.html +19 -5
  165. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-logowall.html +18 -6
  166. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-mappoints.html +18 -5
  167. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-matrix.html +24 -6
  168. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-milestones.html +38 -5
  169. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-mindmap.html +35 -12
  170. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-nested.html +13 -5
  171. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-network.html +76 -31
  172. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-notifs.html +21 -3
  173. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-numlist.html +16 -4
  174. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-orgchart.html +36 -16
  175. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-outline.html +17 -3
  176. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-phases.html +23 -5
  177. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-pipeline.html +25 -4
  178. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-pricing.html +36 -8
  179. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-profiles.html +20 -5
  180. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-proscons.html +21 -6
  181. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-pyramid.html +17 -4
  182. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-quad.html +13 -5
  183. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-quotegrid.html +19 -5
  184. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-ranked.html +19 -5
  185. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-roadmap.html +27 -5
  186. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-spectrum.html +29 -6
  187. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-split3.html +24 -5
  188. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-spotlightcard.html +34 -8
  189. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-statgrid.html +17 -5
  190. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-steps.html +25 -4
  191. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-summary.html +22 -5
  192. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-tiercompare.html +29 -6
  193. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-timeline.html +26 -5
  194. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-toc.html +15 -5
  195. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-tree.html +40 -15
  196. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-tricards.html +22 -4
  197. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-venn.html +34 -9
  198. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-overview-vstable.html +24 -5
  199. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-ascend.html +10 -4
  200. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-aurora-bars.html +12 -4
  201. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-beam.html +11 -4
  202. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-boxed.html +26 -13
  203. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-breath.html +14 -3
  204. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-chaptercard.html +13 -4
  205. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-column-light.html +17 -9
  206. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-constellation.html +15 -6
  207. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-countdown.html +10 -4
  208. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-curtain.html +9 -3
  209. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-dawn.html +13 -4
  210. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-depth.html +18 -12
  211. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-divider.html +10 -3
  212. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-drift-grid.html +11 -4
  213. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-duotone.html +16 -4
  214. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-eclipse.html +11 -4
  215. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-emblem.html +12 -5
  216. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-flare-sweep.html +14 -6
  217. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-glow-rule.html +22 -13
  218. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-gradient-word.html +15 -5
  219. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-gridfloor.html +16 -2
  220. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-halo.html +16 -5
  221. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-horizon.html +16 -4
  222. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-index.html +25 -9
  223. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-kinetic.html +10 -3
  224. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-lensflare.html +10 -3
  225. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-liquid.html +12 -5
  226. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-marquee.html +34 -25
  227. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-mesh.html +13 -4
  228. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-monolith.html +9 -3
  229. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-node-title.html +17 -1
  230. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-numeral.html +13 -4
  231. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-orb.html +12 -2
  232. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-orbit.html +9 -1
  233. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-particles.html +7 -1
  234. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-pill.html +7 -1
  235. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-portal.html +14 -2
  236. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-prism.html +15 -2
  237. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-question.html +14 -2
  238. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-quoteopen.html +11 -2
  239. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-reveal-mask.html +6 -1
  240. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-ribbon.html +12 -1
  241. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-ripple.html +8 -1
  242. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-searchbar.html +15 -2
  243. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-slab.html +9 -2
  244. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-sphere-top.html +12 -3
  245. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-split-reveal.html +24 -7
  246. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-splitpanel.html +27 -4
  247. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-spotlight-cone.html +16 -3
  248. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-spotlight.html +10 -2
  249. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-stack.html +17 -4
  250. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-stacked-words.html +14 -3
  251. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-starfield.html +7 -1
  252. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-sweep.html +11 -2
  253. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-tunnel.html +14 -4
  254. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-type-scale.html +9 -2
  255. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-typed.html +9 -2
  256. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-underline-grow.html +13 -1
  257. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-waveform.html +11 -2
  258. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-section-wordmark.html +11 -3
  259. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-annot.html +22 -7
  260. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-beforeafter.html +23 -6
  261. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-bigpercent.html +10 -3
  262. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-bracketstat.html +13 -3
  263. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-bubble.html +7 -3
  264. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-cbars.html +8 -3
  265. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-clockface.html +8 -4
  266. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-column.html +12 -4
  267. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-compass.html +7 -3
  268. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-counterpill.html +11 -3
  269. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-curve.html +22 -10
  270. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-delta.html +13 -4
  271. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-dial.html +9 -3
  272. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-donut.html +15 -4
  273. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-dotgrid.html +14 -4
  274. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-figure.html +9 -3
  275. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-fillshape.html +11 -3
  276. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-fraction.html +11 -5
  277. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-funnel.html +24 -8
  278. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-growtharrow.html +18 -4
  279. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-heatcell.html +8 -3
  280. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-isotype.html +16 -4
  281. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-layered.html +10 -3
  282. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-mega.html +9 -3
  283. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-meter.html +9 -4
  284. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-milestone.html +9 -3
  285. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-money.html +16 -4
  286. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-multiplier.html +12 -4
  287. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-multiring.html +10 -3
  288. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-needle.html +10 -3
  289. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-nodes.html +50 -9
  290. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-odometer.html +9 -4
  291. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-orbitmoons.html +21 -8
  292. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-orbitstat.html +9 -3
  293. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-pair.html +18 -3
  294. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-percentile.html +11 -3
  295. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-pictobar.html +11 -4
  296. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-polar.html +12 -2
  297. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-quotestat.html +15 -5
  298. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-radialbars.html +18 -6
  299. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-range.html +12 -5
  300. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-rank.html +13 -5
  301. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-ratio.html +10 -5
  302. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-ring.html +10 -3
  303. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-risewall.html +18 -2
  304. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-score.html +11 -4
  305. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-segbar.html +9 -3
  306. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-semigauge.html +9 -4
  307. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-sparkline.html +9 -3
  308. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-spectrum.html +9 -2
  309. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-split.html +14 -4
  310. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-stackedbars.html +9 -4
  311. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-tickerstat.html +18 -5
  312. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-tiles.html +18 -4
  313. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-timer.html +14 -4
  314. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-trendline.html +18 -4
  315. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-underscore.html +9 -3
  316. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-versus.html +18 -5
  317. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-waffle.html +18 -4
  318. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier1/t1-stat-waterfall.html +9 -4
  319. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-badge.html +13 -4
  320. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-brace.html +13 -4
  321. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-bracket.html +13 -4
  322. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-bubble.html +16 -4
  323. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-byline.html +17 -5
  324. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-callout.html +13 -4
  325. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-category.html +14 -5
  326. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-checkmark.html +13 -4
  327. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-chipstat.html +13 -4
  328. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-comparemini.html +14 -5
  329. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-coord.html +26 -8
  330. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-counter.html +17 -6
  331. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-crosshair.html +18 -6
  332. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-dashed.html +19 -5
  333. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-definition.html +14 -5
  334. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-deltachip.html +15 -6
  335. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-eyebrow.html +14 -5
  336. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-fact.html +14 -5
  337. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-flag.html +20 -7
  338. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-formula.html +17 -7
  339. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-hash.html +17 -5
  340. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-highlightbox.html +13 -4
  341. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-iconchip.html +13 -4
  342. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-info.html +14 -5
  343. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-infocard.html +13 -4
  344. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-keycap.html +13 -4
  345. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-keyword.html +12 -3
  346. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-leader.html +16 -5
  347. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-legend.html +14 -5
  348. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-measure.html +14 -5
  349. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-meterchip.html +16 -7
  350. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-metric.html +15 -6
  351. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-micropie.html +15 -6
  352. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-minibar.html +15 -6
  353. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-node.html +28 -18
  354. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-note.html +14 -5
  355. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-numbercallout.html +18 -6
  356. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-percent.html +15 -6
  357. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-pointer.html +31 -22
  358. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-price.html +20 -8
  359. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-progress.html +15 -6
  360. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-pulse.html +14 -5
  361. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-qmark.html +15 -6
  362. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-quote.html +16 -7
  363. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-rating.html +14 -5
  364. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-ribbon.html +19 -6
  365. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-ringmark.html +27 -17
  366. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-search.html +14 -5
  367. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-sparkchip.html +15 -6
  368. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-spec.html +25 -13
  369. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-status.html +15 -6
  370. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-step.html +16 -7
  371. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-tag.html +14 -5
  372. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-takeaway.html +13 -4
  373. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-tcode.html +18 -6
  374. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-timestamp.html +15 -6
  375. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-toast.html +15 -6
  376. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-tooltip.html +14 -5
  377. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-underline.html +14 -5
  378. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lb-warning.html +16 -7
  379. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-avatar.html +12 -5
  380. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-badgecard.html +12 -6
  381. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-barslide.html +11 -5
  382. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-bracket.html +20 -7
  383. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-brandbar.html +13 -7
  384. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-bug.html +12 -5
  385. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-capsule.html +14 -5
  386. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-chapterbar.html +12 -5
  387. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-chip.html +11 -5
  388. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-cinemastrip.html +20 -8
  389. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-cite.html +12 -6
  390. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-clockname.html +11 -5
  391. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-coordbar.html +16 -6
  392. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-corner.html +11 -5
  393. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-cornerframe.html +16 -6
  394. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-countbar.html +13 -7
  395. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-dividername.html +26 -6
  396. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-dotline.html +11 -5
  397. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-doublecard.html +12 -6
  398. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-dual.html +12 -6
  399. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-edgebar.html +11 -5
  400. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-flipcard.html +15 -5
  401. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-geopin.html +11 -5
  402. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-glasscard.html +11 -5
  403. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-gradient.html +11 -5
  404. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-gradientbar.html +12 -4
  405. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-handlebar.html +11 -5
  406. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-infochips.html +11 -5
  407. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-lensbar.html +25 -12
  408. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-live.html +12 -6
  409. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-location.html +14 -6
  410. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-logolock.html +14 -6
  411. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-micname.html +14 -6
  412. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-minicard.html +13 -5
  413. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-minimal.html +13 -5
  414. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-nameplate3d.html +13 -5
  415. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-neonframe.html +14 -5
  416. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-neonpill.html +13 -6
  417. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-nowplaying.html +15 -7
  418. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-orbitbadge.html +13 -5
  419. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-pill.html +14 -6
  420. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-portraitframe.html +14 -6
  421. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-progressname.html +13 -5
  422. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-quote.html +15 -7
  423. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-ratingname.html +14 -6
  424. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-ribbon.html +13 -5
  425. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-sidebracket.html +13 -5
  426. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-slidetab.html +15 -6
  427. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-social.html +14 -6
  428. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-spotlightname.html +14 -6
  429. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-stacked.html +13 -5
  430. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-statline.html +13 -5
  431. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-tagrow.html +14 -5
  432. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-thinstrip.html +18 -8
  433. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-ticker.html +29 -7
  434. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-titlebar.html +19 -5
  435. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-trust.html +16 -8
  436. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-typedname.html +13 -5
  437. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-underbar.html +13 -5
  438. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/cards/tier2/t2-lt-waveband.html +14 -6
  439. package/skills/luckiest-video-studio-cards/packs/02-aurora-glass/tokens.css +18 -8
  440. package/skills/luckiest-video-studio-cards/packs/_blueprint/cards/tier1/t1-stat.html +4 -2
  441. package/skills/luckiest-video-studio-cards/packs/_blueprint/cards/tier2/t2-list.html +5 -2
  442. package/skills/luckiest-video-studio-cards/packs/_blueprint/tokens.css +12 -4
  443. package/skills/luckiest-video-studio-cards/references/card-contract.md +15 -1
  444. package/skills/luckiest-video-studio-cards/scripts/qa-cards.mjs +14 -8
  445. package/skills/luckiest-video-studio-cards/templates/glass-popout/index.html +35 -11
  446. package/skills/luckiest-video-studio-cards/templates/glass-popout/meta.json +6 -1
  447. package/skills/luckiest-video-studio-cards/templates/graph-paper/index.html +15 -10
  448. package/skills/luckiest-video-studio-cards/templates/graph-paper/meta.json +7 -2
  449. package/skills/luckiest-video-studio-motion/ATTRIBUTION.md +4 -0
  450. package/skills/luckiest-video-studio-motion/CHANGELOG.md +9 -0
  451. package/skills/luckiest-video-studio-motion/SKILL.md +7 -3
  452. package/skills/luckiest-video-studio-motion/references/vocabulary.md +21 -0
  453. package/skills/luckiest-video-studio-motion/templates/spring.js +50 -0
  454. package/skills/luckiest-video-studio-motion/templates/spring.test.mjs +51 -0
  455. package/skills/luckiest-video-studio-recreate/ATTRIBUTION.md +2 -0
  456. package/skills/luckiest-video-studio-recreate/CHANGELOG.md +11 -0
  457. package/skills/luckiest-video-studio-recreate/SKILL.md +46 -7
  458. package/skills/luckiest-video-studio-reel/CHANGELOG.md +6 -0
  459. package/skills/luckiest-video-studio-reel/SKILL.md +9 -4
  460. package/skills/luckiest-video-studio-reel/templates/reel.html +36 -18
  461. package/skills/luckiest-video-studio-reference/CHANGELOG.md +4 -0
  462. package/skills/luckiest-video-studio-reference/SKILL.md +10 -3
  463. package/skills/luckiest-video-studio-review/ATTRIBUTION.md +4 -0
  464. package/skills/luckiest-video-watcher/ATTRIBUTION.md +37 -0
  465. package/skills/luckiest-video-watcher/CHANGELOG.md +90 -0
  466. package/skills/luckiest-video-watcher/LICENSE-claude-video +21 -0
  467. package/skills/luckiest-video-watcher/SKILL.md +201 -0
  468. package/skills/luckiest-video-watcher/references/watch-guide.md +159 -0
  469. package/skills/luckiest-video-watcher/scripts/config.py +174 -0
  470. package/skills/luckiest-video-watcher/scripts/download.py +207 -0
  471. package/skills/luckiest-video-watcher/scripts/frames.py +758 -0
  472. package/skills/luckiest-video-watcher/scripts/gemini.py +162 -0
  473. package/skills/luckiest-video-watcher/scripts/local_whisperx.py +147 -0
  474. package/skills/luckiest-video-watcher/scripts/runtime.py +35 -0
  475. package/skills/luckiest-video-watcher/scripts/setup.py +338 -0
  476. package/skills/luckiest-video-watcher/scripts/transcribe.py +113 -0
  477. package/skills/luckiest-video-watcher/scripts/watch.py +430 -0
  478. package/skills/luckiest-video-watcher/scripts/whisper.py +425 -0
@@ -0,0 +1,758 @@
1
+ #!/usr/bin/env python3
2
+ """Probe video metadata and extract frames at an auto-scaled fps.
3
+
4
+ Auto-fps targets a frame budget, not a fixed rate. Token cost scales with frame
5
+ count, so budget-by-duration keeps short videos dense and long videos capped.
6
+ When a user-specified range is passed, focused-mode budgets denser (they are
7
+ zooming in for detail).
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import math
13
+ from functools import lru_cache
14
+ import re
15
+ import shutil
16
+ import subprocess
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ from runtime import configure_stdio, run_text, diagnostic
21
+
22
+
23
+ MAX_FPS = 2.0
24
+ SCENE_THRESHOLD = 0.20
25
+ # Keep scene-detection results once we have at least this many distinct shots.
26
+ # Below this the video is effectively static (screen recording, talking head),
27
+ # so we fall back to uniform sampling. Matching the reference fork's behaviour,
28
+ # this is a low floor — NOT the frame budget — so normal videos with cuts use
29
+ # the (single-pass) scene engine instead of paying for a wasted second decode.
30
+ SCENE_MIN_FRAMES = 8
31
+ # Below this many decoded keyframes a clip is too sparse for keyframe coverage
32
+ # (very short or oddly encoded), so the cheap tier falls back to uniform.
33
+ KEYFRAME_MIN = 4
34
+ MAX_READ_DIMENSION = 1998
35
+ # Frame-delta dedup: downscale each frame to a DEDUP_THUMB x DEDUP_THUMB
36
+ # RGB thumbnail and treat two frames as near-identical when their mean
37
+ # per-channel difference (0-255) is at or below DEDUP_THRESHOLD. This catches
38
+ # held slides, but subtle code/text changes can be missed at this thumbnail size. Unlike a within-frame
39
+ # perceptual hash, this distinguishes flat frames (solid slides, fades) by color.
40
+ DEDUP_THUMB = 16
41
+ DEDUP_THRESHOLD = 2.0
42
+ SHOWINFO_TS_RE = re.compile(r"pts_time:([^\s]+)")
43
+
44
+
45
+ def _scale_filter(resolution: int) -> str:
46
+ return (
47
+ f"scale=w='min({resolution},iw)':h='min({MAX_READ_DIMENSION},ih)':"
48
+ "force_original_aspect_ratio=decrease:force_divisible_by=2"
49
+ )
50
+
51
+
52
+ def _clamp_fps(fps: float, duration_seconds: float, max_frames: int) -> tuple[float, int]:
53
+ fps = min(fps, MAX_FPS)
54
+ target = min(max_frames, max(1, int(round(fps * duration_seconds))))
55
+ return fps, target
56
+
57
+
58
+ def parse_time(value: str | float | int | None) -> float | None:
59
+ """Parse a finite non-negative SS, MM:SS, or HH:MM:SS time."""
60
+ if value is None or str(value).strip() == "":
61
+ return None
62
+ try:
63
+ parts = str(value).strip().split(":")
64
+ if len(parts) > 3:
65
+ raise ValueError()
66
+ numbers = [float(part) for part in parts]
67
+ if any(not math.isfinite(n) or n < 0 for n in numbers):
68
+ raise ValueError()
69
+ if len(parts) > 1 and (any(n >= 60 for n in numbers[1:]) or any(n != int(n) for n in numbers[:-1])):
70
+ raise ValueError()
71
+ return sum(n * 60 ** i for i, n in enumerate(reversed(numbers)))
72
+ except ValueError:
73
+ raise SystemExit("Invalid time; expected finite non-negative SS, MM:SS, or HH:MM:SS.") from None
74
+
75
+
76
+ def format_time(seconds: float) -> str:
77
+ total = int(round(seconds))
78
+ hours, rem = divmod(total, 3600)
79
+ minutes, sec = divmod(rem, 60)
80
+ if hours:
81
+ return f"{hours}:{minutes:02d}:{sec:02d}"
82
+ return f"{minutes:02d}:{sec:02d}"
83
+
84
+
85
+ def get_metadata(video_path: str) -> dict:
86
+ result = run_text([
87
+ "ffprobe", "-v", "error", "-print_format", "json", "-show_format", "-show_streams",
88
+ str(Path(video_path).resolve()),
89
+ ], timeout=30)
90
+ if result.returncode != 0:
91
+ raise SystemExit(f"ffprobe failed: {diagnostic(result.stderr)}")
92
+ try:
93
+ data = json.loads(result.stdout)
94
+ streams, fmt = data["streams"], data.get("format", {})
95
+ if not isinstance(streams, list) or not streams:
96
+ raise ValueError()
97
+ video = next((s for s in streams if s.get("codec_type") == "video"), {})
98
+ audio = next((s for s in streams if s.get("codec_type") == "audio"), {})
99
+ # Some containers include a nonzero start PTS in format.duration.
100
+ # Stream durations describe elapsed source time and avoid overestimating
101
+ # a three-second clip whose first packet starts at seven seconds.
102
+ elapsed = [float(s['duration']) for s in (video, audio) if s.get('duration') not in (None, 'N/A')]
103
+ duration = max(elapsed) if elapsed else float(fmt.get('duration'))
104
+ if any(not math.isfinite(d) or d <= 0 for d in elapsed) or not math.isfinite(duration) or duration <= 0:
105
+ raise ValueError()
106
+ return {
107
+ "duration_seconds": duration, "width": video.get("width"), "height": video.get("height"),
108
+ "codec": video.get("codec_name"), "size_bytes": int(fmt.get("size") or 0),
109
+ "has_audio": bool(audio), "has_video": bool(video),
110
+ }
111
+ except (ValueError, TypeError, KeyError, AttributeError):
112
+ raise SystemExit(f"ffprobe returned invalid metadata, streams, or duration: {diagnostic(result.stderr)}") from None
113
+
114
+
115
+ @lru_cache(maxsize=8)
116
+ def _sync_option(executable: str) -> tuple[str, str]:
117
+ result = run_text([executable, "-hide_banner", "-h", "full"], timeout=30)
118
+ if result.returncode == 0:
119
+ help_text = result.stdout + result.stderr
120
+ # FFmpeg 9 lists options with stream specifiers, e.g. "-fps_mode[:<stream_spec>]".
121
+ if re.search(r"(?m)^\s*-fps_mode\b", help_text):
122
+ return "-fps_mode", "vfr"
123
+ if re.search(r"(?m)^\s*-vsync\b", help_text):
124
+ return "-vsync", "vfr"
125
+ raise SystemExit(f"{executable} did not advertise -fps_mode or -vsync; check the FFmpeg installation. {diagnostic(result.stderr)}")
126
+
127
+
128
+ def sync_args() -> tuple[str, str]:
129
+ return _sync_option(shutil.which("ffmpeg") or "ffmpeg")
130
+
131
+
132
+ def validate_controls(resolution, max_frames, start_seconds, end_seconds, fps=None):
133
+ if not isinstance(resolution, int) or resolution < 2:
134
+ raise SystemExit("--resolution must be an integer of at least 2 pixels")
135
+ if max_frames is not None and (not isinstance(max_frames, int) or max_frames < 1):
136
+ raise SystemExit("--max-frames must be a positive integer")
137
+ start, end = parse_time(start_seconds) or 0.0, parse_time(end_seconds)
138
+ if end is not None and end <= start:
139
+ raise SystemExit("--end must be greater than --start")
140
+ if fps is not None and (not math.isfinite(fps) or fps <= 0):
141
+ raise SystemExit("--fps must be finite and greater than zero")
142
+
143
+
144
+ def _emitted_frames(files, stderr, offset, reason):
145
+ try:
146
+ stamps = [float(m.group(1)) for m in SHOWINFO_TS_RE.finditer(stderr)]
147
+ # FFmpeg may log an extra filtered frame before an output cap stops it.
148
+ if len(stamps) < len(files) or any(not math.isfinite(t) for t in stamps):
149
+ raise ValueError()
150
+ if any(b < a for a, b in zip(stamps, stamps[1:])):
151
+ raise ValueError()
152
+ except ValueError:
153
+ raise SystemExit("ffmpeg output is missing valid frame timestamps; refusing to mislabel images.") from None
154
+ return [{"index": i, "timestamp_seconds": round(offset + stamps[i], 6),
155
+ "path": str(path), "reason": reason} for i, path in enumerate(files)]
156
+
157
+
158
+ def auto_fps(duration_seconds: float, max_frames: int = 100) -> tuple[float, int]:
159
+ """Pick fps that targets a sensible frame budget for full-video scans."""
160
+ if duration_seconds <= 0:
161
+ return 1.0, 1
162
+
163
+ if duration_seconds <= 30:
164
+ target = min(max_frames, max(12, int(round(duration_seconds))))
165
+ elif duration_seconds <= 60:
166
+ target = min(max_frames, 40)
167
+ elif duration_seconds <= 180: # 3 min
168
+ target = min(max_frames, 60)
169
+ elif duration_seconds <= 600: # 10 min
170
+ target = min(max_frames, 80)
171
+ else:
172
+ target = max_frames
173
+
174
+ return _clamp_fps(target / duration_seconds, duration_seconds, max_frames)
175
+
176
+
177
+ def auto_fps_focus(duration_seconds: float, max_frames: int = 100) -> tuple[float, int]:
178
+ """Denser budget for user-specified ranges — they are zooming in for detail."""
179
+ if duration_seconds <= 0:
180
+ return min(MAX_FPS, 2.0), 2
181
+
182
+ if duration_seconds <= 5:
183
+ target = min(max_frames, max(10, int(round(duration_seconds * 6))))
184
+ elif duration_seconds <= 15:
185
+ target = min(max_frames, max(30, int(round(duration_seconds * 4))))
186
+ elif duration_seconds <= 30:
187
+ target = min(max_frames, 60)
188
+ elif duration_seconds <= 60:
189
+ target = min(max_frames, 80)
190
+ elif duration_seconds <= 180:
191
+ target = max_frames
192
+ else:
193
+ target = max_frames
194
+
195
+ return _clamp_fps(target / duration_seconds, duration_seconds, max_frames)
196
+
197
+
198
+ def extract(
199
+ video_path: str, out_dir: Path, fps: float, resolution: int = 512,
200
+ max_frames: int = 100, start_seconds: float | None = None,
201
+ end_seconds: float | None = None,
202
+ ) -> list[dict]:
203
+ validate_controls(resolution, max_frames, start_seconds, end_seconds, fps)
204
+ duration = get_metadata(video_path)["duration_seconds"]
205
+ start = start_seconds or 0.0
206
+ end = min(duration, end_seconds) if end_seconds is not None else duration
207
+ if end <= start:
208
+ raise SystemExit("Requested range is outside the video duration")
209
+ rate = min(fps, MAX_FPS, max_frames / (end - start))
210
+ out_dir.mkdir(parents=True, exist_ok=True)
211
+ for existing in out_dir.glob("frame_*.jpg"):
212
+ existing.unlink()
213
+ # Accurate input seeking resets t relative to the seek point. select keeps
214
+ # the first real frame in each time bucket; fps would shift the pixels.
215
+ vf = (f"select='isnan(prev_selected_t)+gt(floor(t*{rate:.17g}),"
216
+ f"floor(prev_selected_t*{rate:.17g}))',{_scale_filter(resolution)},showinfo")
217
+ result = run_text([
218
+ "ffmpeg", "-hide_banner", "-loglevel", "info", "-y",
219
+ "-ss", f"{start:.6f}", "-to", f"{end:.6f}", "-i", str(Path(video_path).resolve()),
220
+ "-vf", vf, *sync_args(), "-frames:v", str(max_frames), "-q:v", "4",
221
+ str(out_dir / "frame_%04d.jpg"),
222
+ ])
223
+ if result.returncode != 0:
224
+ raise SystemExit(f"ffmpeg frame extraction failed: {diagnostic(result.stderr)}")
225
+ files = sorted(out_dir.glob("frame_*.jpg"))
226
+ if not files:
227
+ raise SystemExit("ffmpeg produced no video frames in the requested range")
228
+ return _emitted_frames(files, result.stderr, start, "uniform")
229
+
230
+
231
+ def extract_scene_candidates(
232
+ video_path: str,
233
+ out_dir: Path,
234
+ resolution: int = 512,
235
+ max_frames: int | None = 100,
236
+ start_seconds: float | None = None,
237
+ end_seconds: float | None = None,
238
+ threshold: float = SCENE_THRESHOLD,
239
+ ) -> list[dict]:
240
+ """Extract first frame plus ffmpeg scene-change frames.
241
+
242
+ When ``max_frames`` is set, ``-frames:v`` lets ffmpeg stop decoding once it
243
+ has emitted that many frames (early exit) and avoids writing extras that we
244
+ would only delete afterwards. ``None`` (uncapped "complete" detail) keeps
245
+ every detected shot, as the user explicitly opted in.
246
+ """
247
+ validate_controls(resolution, max_frames, start_seconds, end_seconds)
248
+ if shutil.which("ffmpeg") is None:
249
+ raise SystemExit("ffmpeg is not installed. Install with: brew install ffmpeg")
250
+
251
+ out_dir.mkdir(parents=True, exist_ok=True)
252
+ for existing in out_dir.glob("frame_*.jpg"):
253
+ existing.unlink()
254
+
255
+ output_pattern = str(out_dir / "frame_%04d.jpg")
256
+ cmd: list[str] = [
257
+ "ffmpeg",
258
+ "-hide_banner",
259
+ "-loglevel", "info",
260
+ "-y",
261
+ ]
262
+ if start_seconds is not None:
263
+ cmd += ["-ss", f"{start_seconds:.6f}"]
264
+ if end_seconds is not None:
265
+ cmd += ["-to", f"{end_seconds:.6f}"]
266
+
267
+ vf = f"select='eq(n\\,0)+gt(scene\\,{threshold})',{_scale_filter(resolution)},showinfo"
268
+ cmd += [
269
+ "-i", str(Path(video_path).resolve()),
270
+ "-vf", vf,
271
+ *sync_args(),
272
+ ]
273
+ if max_frames is not None:
274
+ cmd += ["-frames:v", str(max_frames)]
275
+ cmd += [
276
+ "-q:v", "4",
277
+ output_pattern,
278
+ ]
279
+ result = run_text(cmd)
280
+ if result.returncode != 0:
281
+ raise SystemExit(f"ffmpeg scene extraction failed: {result.stderr.strip()}")
282
+
283
+ out = _emitted_frames(sorted(out_dir.glob("frame_*.jpg")), result.stderr, start_seconds or 0.0, "scene-change")
284
+ if out:
285
+ out[0]["reason"] = "first-frame"
286
+ return out
287
+
288
+
289
+ def _even_indices(count: int, n: int) -> list[int]:
290
+ """Indices of ``n`` evenly-spaced items out of ``count`` (first + last kept).
291
+
292
+ ``n >= count`` returns every index; ``n == 1`` returns just the first.
293
+ """
294
+ if n >= count:
295
+ return list(range(count))
296
+ if n <= 1:
297
+ return [0]
298
+ return [round(i * (count - 1) / (n - 1)) for i in range(n)]
299
+
300
+
301
+ def parse_timestamps(value: str | None) -> list[float]:
302
+ """Parse a comma-separated list of times (SS, MM:SS, HH:MM:SS) into a
303
+ sorted, de-duplicated list of seconds. Empty/blank tokens are skipped;
304
+ an unparseable token raises (via :func:`parse_time`)."""
305
+ if not value:
306
+ return []
307
+ out: list[float] = []
308
+ for token in value.split(","):
309
+ token = token.strip()
310
+ if not token:
311
+ continue
312
+ seconds = parse_time(token)
313
+ if seconds is not None:
314
+ out.append(float(seconds))
315
+ return sorted(set(out))
316
+
317
+
318
+ def merge_frames(primary: list[dict], pinned: list[dict]) -> list[dict]:
319
+ """Combine two frame lists into one chronological list and reindex 0..n-1.
320
+
321
+ ``pinned`` frames (transcript cues) are never dropped — this is a plain
322
+ union, so the cap is enforced upstream by reserving budget for the cues.
323
+ """
324
+ merged = sorted([*primary, *pinned], key=lambda f: f["timestamp_seconds"])
325
+ for i, frame in enumerate(merged):
326
+ frame["index"] = i
327
+ return merged
328
+
329
+
330
+ def extract_at_timestamps(
331
+ video_path: str,
332
+ out_dir: Path,
333
+ timestamps: list[float],
334
+ resolution: int = 512,
335
+ max_frames: int | None = None,
336
+ start_seconds: float | None = None,
337
+ end_seconds: float | None = None,
338
+ ) -> tuple[list[dict], dict]:
339
+ """Grab exactly one frame at each requested timestamp (transcript cues).
340
+
341
+ Timestamps are absolute source seconds. Any falling outside an active
342
+ ``[start, end]`` focus window are dropped. Files use a ``cue_*.jpg`` prefix
343
+ so they sit alongside detail-engine ``frame_*.jpg`` output without either
344
+ clobbering the other. When more cues than ``max_frames`` survive, they are
345
+ even-sampled (first + last kept) before extraction.
346
+ """
347
+ validate_controls(resolution, max_frames, start_seconds, end_seconds)
348
+ if shutil.which("ffmpeg") is None:
349
+ raise SystemExit("ffmpeg is not installed. Install with: brew install ffmpeg")
350
+
351
+ out_dir.mkdir(parents=True, exist_ok=True)
352
+ for existing in out_dir.glob("cue_*.jpg"):
353
+ existing.unlink()
354
+
355
+ lo = start_seconds or 0.0
356
+ hi = end_seconds if end_seconds is not None else float("inf")
357
+ requested = sorted(set(parse_time(t) for t in timestamps))
358
+ in_window = [t for t in requested if lo <= t <= hi]
359
+ dropped = len(requested) - len(in_window)
360
+
361
+ if max_frames is not None and len(in_window) > max_frames:
362
+ points = [in_window[i] for i in _even_indices(len(in_window), max_frames)]
363
+ else:
364
+ points = in_window
365
+
366
+ out: list[dict] = []
367
+ for t in points:
368
+ path = out_dir / f"cue_{len(out):04d}.jpg"
369
+ cmd = [
370
+ "ffmpeg", "-hide_banner", "-loglevel", "info", "-y",
371
+ "-ss", f"{t:.6f}", "-i", str(Path(video_path).resolve()),
372
+ "-frames:v", "1", "-vf", f"{_scale_filter(resolution)},showinfo",
373
+ *sync_args(), "-q:v", "4", str(path),
374
+ ]
375
+ result = run_text(cmd)
376
+ if result.returncode != 0:
377
+ raise SystemExit(f"ffmpeg cue extraction failed: {diagnostic(result.stderr)}")
378
+ if path.exists():
379
+ frame = _emitted_frames([path], result.stderr, t, "transcript-cue")[0]
380
+ frame["requested_timestamp_seconds"] = t
381
+ frame["index"] = len(out)
382
+ if frame["timestamp_seconds"] <= hi:
383
+ out.append(frame)
384
+ else:
385
+ path.unlink()
386
+
387
+ meta = {
388
+ "engine": "timestamps",
389
+ "candidate_count": len(requested),
390
+ "selected_count": len(out),
391
+ "dropped_out_of_window": dropped,
392
+ "fallback": False,
393
+ }
394
+ return out, meta
395
+
396
+
397
+ def _even_sample(candidates: list[dict], n: int) -> list[dict]:
398
+ """Pick ``n`` evenly-spaced candidates (always including first and last),
399
+ delete the JPEGs we drop, and reindex the survivors 0..len-1.
400
+
401
+ Shared by every capped engine so all detail modes sample the same way:
402
+ detect all candidates across the full range, then thin down to the cap.
403
+ ``n >= len(candidates)`` keeps everything (the uncapped / under-cap case).
404
+ """
405
+ selected = [candidates[i] for i in _even_indices(len(candidates), n)]
406
+
407
+ keep_paths = {sel["path"] for sel in selected}
408
+ for cand in candidates:
409
+ if cand["path"] not in keep_paths:
410
+ try:
411
+ Path(cand["path"]).unlink()
412
+ except OSError:
413
+ pass
414
+ for i, frame in enumerate(selected):
415
+ frame["index"] = i
416
+ return selected
417
+
418
+
419
+ def _frame_delta(a: bytes, b: bytes) -> float:
420
+ """Mean absolute per-pixel difference (0-255) between two RGB
421
+ thumbnails. Mismatched lengths are treated as maximally different so a
422
+ decode hiccup never collapses distinct frames."""
423
+ if not a or len(a) != len(b):
424
+ return float("inf")
425
+ return sum(abs(x - y) for x, y in zip(a, b)) / len(a)
426
+
427
+
428
+ def _thumb_frames(paths: list[Path]) -> list[bytes]:
429
+ """Decode every frame in ``paths`` to a small RGB thumbnail via one
430
+ ffmpeg pass over the JPEG sequence.
431
+
432
+ ffmpeg does the pixel decode (keeps us pure-stdlib); we slice the raw
433
+ RGB stream into one ``DEDUP_THUMB``-square thumbnail per frame.
434
+ Fail-open: any ffmpeg error, an unrecognized name, or a byte-count mismatch
435
+ returns ``[]`` so the caller skips dedup rather than breaking extraction.
436
+ """
437
+ if not paths:
438
+ return []
439
+ paths = [Path(p) for p in paths]
440
+ m = re.match(r"(.*?)(\d+)(\.[A-Za-z0-9]+)$", paths[0].name)
441
+ if m is None:
442
+ return []
443
+ prefix, digits, ext = m.group(1), m.group(2), m.group(3)
444
+ pattern = str(paths[0].parent / f"{prefix}%0{len(digits)}d{ext}")
445
+
446
+ cmd = [
447
+ "ffmpeg",
448
+ "-hide_banner",
449
+ "-loglevel", "error",
450
+ "-start_number", str(int(digits)),
451
+ "-i", pattern,
452
+ "-vf", f"scale={DEDUP_THUMB}:{DEDUP_THUMB},format=rgb24",
453
+ "-f", "rawvideo",
454
+ "-",
455
+ ]
456
+ try:
457
+ result = subprocess.run(cmd, capture_output=True)
458
+ except OSError:
459
+ return []
460
+ if result.returncode != 0:
461
+ return []
462
+
463
+ chunk = DEDUP_THUMB * DEDUP_THUMB * 3
464
+ data = result.stdout
465
+ if len(data) != chunk * len(paths):
466
+ return []
467
+ return [data[i * chunk:(i + 1) * chunk] for i in range(len(paths))]
468
+
469
+
470
+ def dedupe_perceptual(
471
+ candidates: list[dict], threshold: float = DEDUP_THRESHOLD
472
+ ) -> tuple[list[dict], int]:
473
+ """Drop near-identical frames from a chronological candidate list.
474
+
475
+ Thumbnails the extracted JPEGs and greedily removes frames whose mean
476
+ per-pixel difference from the last kept one is within ``threshold``. Returns
477
+ ``(survivors, dropped_count)``; a no-op (unchanged list) when thumbnails are
478
+ unavailable or there are fewer than two candidates.
479
+ """
480
+ if len(candidates) <= 1:
481
+ return candidates, 0
482
+ thumbs = _thumb_frames([Path(c["path"]) for c in candidates])
483
+ return _dedupe_by_deltas(candidates, thumbs, threshold)
484
+
485
+
486
+ def _dedupe_by_deltas(
487
+ candidates: list[dict], thumbs: list[bytes], threshold: float = DEDUP_THRESHOLD
488
+ ) -> tuple[list[dict], int]:
489
+ """Greedily drop frames within ``threshold`` mean per-pixel difference of the
490
+ last *kept* frame. Deletes dropped JPEGs and reindexes survivors 0..n-1 (same
491
+ cleanup contract as :func:`_even_sample`). Fail-open: if ``thumbs`` does not
492
+ line up 1:1 with ``candidates``, return them unchanged.
493
+ """
494
+ if len(thumbs) != len(candidates) or len(candidates) <= 1:
495
+ return candidates, 0
496
+
497
+ kept = [candidates[0]]
498
+ last = thumbs[0]
499
+ dropped: list[dict] = []
500
+ for cand, thumb in zip(candidates[1:], thumbs[1:]):
501
+ if _frame_delta(thumb, last) <= threshold:
502
+ dropped.append(cand)
503
+ else:
504
+ kept.append(cand)
505
+ last = thumb
506
+
507
+ for cand in dropped:
508
+ try:
509
+ Path(cand["path"]).unlink()
510
+ except OSError:
511
+ pass
512
+ for i, frame in enumerate(kept):
513
+ frame["index"] = i
514
+ return kept, len(dropped)
515
+
516
+
517
+ def extract_scene_or_uniform(
518
+ video_path: str,
519
+ out_dir: Path,
520
+ fps: float,
521
+ target_frames: int,
522
+ resolution: int = 512,
523
+ max_frames: int | None = 100,
524
+ start_seconds: float | None = None,
525
+ end_seconds: float | None = None,
526
+ dedup: bool = True,
527
+ ) -> tuple[list[dict], dict]:
528
+ """Prefer scene selection, falling back to uniform only when the video is
529
+ effectively static (fewer than ``SCENE_MIN_FRAMES`` detected shots).
530
+
531
+ Scene cuts are detected across the *whole* range (uncapped), near-identical
532
+ frames are dropped (:func:`dedupe_perceptual`, unless ``dedup`` is False),
533
+ and the survivors are even-sampled down to ``max_frames`` via
534
+ :func:`_even_sample`, exactly like the keyframe engine. This costs a full
535
+ decode, but it guarantees coverage spans the entire clip — capping detection
536
+ with ``-frames:v`` instead would keep only the first ``max_frames`` cuts and
537
+ drop the tail of long videos (and could even fall below ``SCENE_MIN_FRAMES``
538
+ and misfire the uniform fallback on a cut-heavy clip).
539
+ """
540
+ scene_frames = extract_scene_candidates(
541
+ video_path,
542
+ out_dir,
543
+ resolution=resolution,
544
+ max_frames=None,
545
+ start_seconds=start_seconds,
546
+ end_seconds=end_seconds,
547
+ )
548
+ scene_count = len(scene_frames)
549
+ if scene_count >= SCENE_MIN_FRAMES:
550
+ deduped, n_dropped = dedupe_perceptual(scene_frames) if dedup else (scene_frames, 0)
551
+ cap = len(deduped) if max_frames is None else max_frames
552
+ selected = _even_sample(deduped, cap)
553
+ return selected, {
554
+ "engine": "scene",
555
+ "candidate_count": scene_count,
556
+ "deduped_count": n_dropped,
557
+ "selected_count": len(selected),
558
+ "fallback": False,
559
+ }
560
+
561
+ fallback_cap = target_frames if max_frames is None else min(max_frames, target_frames)
562
+ frames = extract(
563
+ video_path,
564
+ out_dir,
565
+ fps=fps,
566
+ resolution=resolution,
567
+ max_frames=fallback_cap,
568
+ start_seconds=start_seconds,
569
+ end_seconds=end_seconds,
570
+ )
571
+ uniform_count = len(frames)
572
+ n_dropped = 0
573
+ if dedup:
574
+ frames, n_dropped = dedupe_perceptual(frames)
575
+ return frames, {
576
+ "engine": "uniform",
577
+ "candidate_count": uniform_count,
578
+ "deduped_count": n_dropped,
579
+ "selected_count": len(frames),
580
+ "fallback": True,
581
+ }
582
+
583
+
584
+ def extract_keyframes(
585
+ video_path: str,
586
+ out_dir: Path,
587
+ resolution: int = 512,
588
+ max_frames: int | None = 50,
589
+ start_seconds: float | None = None,
590
+ end_seconds: float | None = None,
591
+ dedup: bool = True,
592
+ ) -> tuple[list[dict], dict]:
593
+ """Decode only keyframes (I-frames) — the cheap, near-instant tier.
594
+
595
+ ``-skip_frame nokey`` makes ffmpeg reconstruct only keyframes, skipping all
596
+ P/B frames. Encoders emit keyframes at scene cuts, so these already
597
+ approximate "distinct moments". Near-identical frames are dropped
598
+ (:func:`dedupe_perceptual`, unless ``dedup`` is False); over-cap →
599
+ even-sample first→last; too few keyframes → uniform fallback.
600
+ """
601
+ validate_controls(resolution, max_frames, start_seconds, end_seconds)
602
+ if shutil.which("ffmpeg") is None:
603
+ raise SystemExit("ffmpeg is not installed. Install with: brew install ffmpeg")
604
+
605
+ out_dir.mkdir(parents=True, exist_ok=True)
606
+ for existing in out_dir.glob("frame_*.jpg"):
607
+ existing.unlink()
608
+
609
+ output_pattern = str(out_dir / "frame_%04d.jpg")
610
+ cmd: list[str] = [
611
+ "ffmpeg",
612
+ "-hide_banner",
613
+ "-loglevel", "info",
614
+ "-y",
615
+ ]
616
+ if start_seconds is not None:
617
+ cmd += ["-ss", f"{start_seconds:.6f}"]
618
+ if end_seconds is not None:
619
+ cmd += ["-to", f"{end_seconds:.6f}"]
620
+ cmd += [
621
+ "-skip_frame", "nokey",
622
+ "-i", str(Path(video_path).resolve()),
623
+ "-vf", f"{_scale_filter(resolution)},showinfo",
624
+ *sync_args(),
625
+ "-q:v", "4",
626
+ output_pattern,
627
+ ]
628
+ result = run_text(cmd)
629
+ files = sorted(out_dir.glob("frame_*.jpg"))
630
+ if result.returncode != 0:
631
+ if files:
632
+ raise SystemExit(f"ffmpeg keyframe extraction failed after partial output: {diagnostic(result.stderr)}")
633
+ print(f"[watch] no keyframe candidates; trying normal decoding. {diagnostic(result.stderr, 700)}", file=sys.stderr)
634
+ candidates = _emitted_frames(files, result.stderr, start_seconds or 0.0, "keyframe")
635
+
636
+ # Too few keyframes → uniform fallback over the same range.
637
+ if len(candidates) < KEYFRAME_MIN:
638
+ for cand in candidates:
639
+ try:
640
+ Path(cand["path"]).unlink()
641
+ except OSError:
642
+ pass
643
+ meta = get_metadata(video_path)
644
+ full_duration = meta["duration_seconds"]
645
+ eff_start = start_seconds or 0.0
646
+ eff_end = min(end_seconds, full_duration) if end_seconds is not None else full_duration
647
+ eff_duration = max(0.0, eff_end - eff_start)
648
+ budget = max_frames if max_frames is not None else 100
649
+ fps, _ = auto_fps(eff_duration, max_frames=budget)
650
+ frames_out = extract(
651
+ video_path,
652
+ out_dir,
653
+ fps=fps,
654
+ resolution=resolution,
655
+ max_frames=budget,
656
+ start_seconds=start_seconds,
657
+ end_seconds=end_seconds,
658
+ )
659
+ uniform_count = len(frames_out)
660
+ n_dropped = 0
661
+ if dedup:
662
+ frames_out, n_dropped = dedupe_perceptual(frames_out)
663
+ return frames_out, {
664
+ "engine": "uniform",
665
+ "candidate_count": uniform_count,
666
+ "deduped_count": n_dropped,
667
+ "selected_count": len(frames_out),
668
+ "fallback": True,
669
+ }
670
+
671
+ # Detect-all, drop near-duplicates, then even-sample down to the cap (first +
672
+ # last always kept). ``max_frames is None`` (uncapped) keeps every keyframe.
673
+ candidate_count = len(candidates)
674
+ deduped, n_dropped = dedupe_perceptual(candidates) if dedup else (candidates, 0)
675
+ cap = len(deduped) if max_frames is None else max_frames
676
+ selected = _even_sample(deduped, cap)
677
+ return selected, {
678
+ "engine": "keyframe",
679
+ "candidate_count": candidate_count,
680
+ "deduped_count": n_dropped,
681
+ "selected_count": len(selected),
682
+ "fallback": False,
683
+ }
684
+
685
+
686
+ if __name__ == "__main__":
687
+ configure_stdio()
688
+ if len(sys.argv) < 3:
689
+ print(
690
+ "usage: frames.py <video-path> <out-dir> [--fps F] [--resolution W] "
691
+ "[--max-frames N] [--start T] [--end T] [--no-dedup]",
692
+ file=sys.stderr,
693
+ )
694
+ raise SystemExit(2)
695
+
696
+ video = sys.argv[1]
697
+ out = Path(sys.argv[2])
698
+ args = sys.argv[3:]
699
+
700
+ fps_override = None
701
+ resolution = 512
702
+ max_frames = 100
703
+ start_arg = None
704
+ end_arg = None
705
+ dedup = True
706
+ i = 0
707
+ while i < len(args):
708
+ if args[i] == "--fps":
709
+ fps_override = float(args[i + 1]); i += 2
710
+ elif args[i] == "--resolution":
711
+ resolution = int(args[i + 1]); i += 2
712
+ elif args[i] == "--max-frames":
713
+ max_frames = int(args[i + 1]); i += 2
714
+ elif args[i] == "--start":
715
+ start_arg = args[i + 1]; i += 2
716
+ elif args[i] == "--end":
717
+ end_arg = args[i + 1]; i += 2
718
+ elif args[i] == "--no-dedup":
719
+ dedup = False; i += 1
720
+ else:
721
+ i += 1
722
+
723
+ meta = get_metadata(video)
724
+ start_sec = parse_time(start_arg)
725
+ end_sec = parse_time(end_arg)
726
+ full_duration = meta["duration_seconds"]
727
+
728
+ effective_start = start_sec if start_sec is not None else 0.0
729
+ effective_end = end_sec if end_sec is not None else full_duration
730
+ effective_duration = max(0.0, effective_end - effective_start)
731
+
732
+ focused = start_sec is not None or end_sec is not None
733
+ if focused:
734
+ fps, target = auto_fps_focus(effective_duration, max_frames=max_frames)
735
+ else:
736
+ fps, target = auto_fps(effective_duration, max_frames=max_frames)
737
+ if fps_override is not None:
738
+ fps = fps_override
739
+ target = max(1, int(round(fps * effective_duration)))
740
+
741
+ frames = extract(
742
+ video, out,
743
+ fps=fps,
744
+ resolution=resolution,
745
+ max_frames=max_frames,
746
+ start_seconds=start_sec,
747
+ end_seconds=end_sec,
748
+ )
749
+ deduped_count = 0
750
+ if dedup:
751
+ frames, deduped_count = dedupe_perceptual(frames)
752
+ print(json.dumps(
753
+ {
754
+ "meta": meta, "fps": fps, "target": target, "focused": focused,
755
+ "deduped_count": deduped_count, "frames": frames,
756
+ },
757
+ indent=2,
758
+ ))