@thunderkiller/video-clipper 1.5.3 → 1.5.4

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 (241) hide show
  1. package/dist/cli.d.ts +5 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +1 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/config/env.d.ts +49 -0
  6. package/dist/config/env.d.ts.map +1 -0
  7. package/dist/config/env.js +1 -0
  8. package/dist/config/env.js.map +1 -0
  9. package/dist/config/index.d.ts +3 -0
  10. package/dist/config/index.d.ts.map +1 -0
  11. package/dist/config/index.js +1 -0
  12. package/dist/config/index.js.map +1 -0
  13. package/dist/index.d.ts +3 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +2 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/lib.d.ts +17 -0
  18. package/dist/lib.d.ts.map +1 -0
  19. package/dist/lib.js +17 -0
  20. package/dist/lib.js.map +1 -0
  21. package/dist/pipeline/runner.d.ts +23 -0
  22. package/dist/pipeline/runner.d.ts.map +1 -0
  23. package/dist/pipeline/runner.js +1 -0
  24. package/dist/pipeline/runner.js.map +1 -0
  25. package/dist/pipeline/stages/audioProcessor.d.ts +20 -0
  26. package/dist/pipeline/stages/audioProcessor.d.ts.map +1 -0
  27. package/dist/pipeline/stages/audioProcessor.js +1 -0
  28. package/dist/pipeline/stages/audioProcessor.js.map +1 -0
  29. package/dist/pipeline/stages/clipExporter.d.ts +15 -0
  30. package/dist/pipeline/stages/clipExporter.d.ts.map +1 -0
  31. package/dist/pipeline/stages/clipExporter.js +1 -0
  32. package/dist/pipeline/stages/clipExporter.js.map +1 -0
  33. package/dist/pipeline/stages/segmentAnalyzer.d.ts +27 -0
  34. package/dist/pipeline/stages/segmentAnalyzer.d.ts.map +1 -0
  35. package/dist/pipeline/stages/segmentAnalyzer.js +1 -0
  36. package/dist/pipeline/stages/segmentAnalyzer.js.map +1 -0
  37. package/dist/pipeline/stages/segmentSelector.d.ts +13 -0
  38. package/dist/pipeline/stages/segmentSelector.d.ts.map +1 -0
  39. package/dist/pipeline/stages/segmentSelector.js +1 -0
  40. package/dist/pipeline/stages/segmentSelector.js.map +1 -0
  41. package/dist/pipeline/stages/videoResolver.d.ts +12 -0
  42. package/dist/pipeline/stages/videoResolver.d.ts.map +1 -0
  43. package/dist/pipeline/stages/videoResolver.js +1 -0
  44. package/dist/pipeline/stages/videoResolver.js.map +1 -0
  45. package/dist/services/audioAnalyzers/base.d.ts +26 -0
  46. package/dist/services/audioAnalyzers/base.d.ts.map +1 -0
  47. package/dist/services/audioAnalyzers/base.js +1 -0
  48. package/dist/services/audioAnalyzers/base.js.map +1 -0
  49. package/dist/services/audioAnalyzers/factory.d.ts +23 -0
  50. package/dist/services/audioAnalyzers/factory.d.ts.map +1 -0
  51. package/dist/services/audioAnalyzers/factory.js +1 -0
  52. package/dist/services/audioAnalyzers/factory.js.map +1 -0
  53. package/dist/services/audioAnalyzers/gemini.d.ts +29 -0
  54. package/dist/services/audioAnalyzers/gemini.d.ts.map +1 -0
  55. package/dist/services/audioAnalyzers/gemini.js +1 -0
  56. package/dist/services/audioAnalyzers/gemini.js.map +1 -0
  57. package/dist/services/audioAnalyzers/index.d.ts +7 -0
  58. package/dist/services/audioAnalyzers/index.d.ts.map +1 -0
  59. package/dist/services/audioAnalyzers/index.js +1 -0
  60. package/dist/services/audioAnalyzers/index.js.map +1 -0
  61. package/dist/services/audioAnalyzers/whisper.d.ts +14 -0
  62. package/dist/services/audioAnalyzers/whisper.d.ts.map +1 -0
  63. package/dist/services/audioAnalyzers/whisper.js +3 -1
  64. package/dist/services/audioAnalyzers/whisper.js.map +1 -0
  65. package/dist/services/audioAnalyzers/yamnet.d.ts +13 -0
  66. package/dist/services/audioAnalyzers/yamnet.d.ts.map +1 -0
  67. package/dist/services/audioAnalyzers/yamnet.js +3 -1
  68. package/dist/services/audioAnalyzers/yamnet.js.map +1 -0
  69. package/dist/services/audioDownloader/index.d.ts +2 -0
  70. package/dist/services/audioDownloader/index.d.ts.map +1 -0
  71. package/dist/services/audioDownloader/index.js +1 -0
  72. package/dist/services/audioDownloader/index.js.map +1 -0
  73. package/dist/services/chunkBuilder/index.d.ts +20 -0
  74. package/dist/services/chunkBuilder/index.d.ts.map +1 -0
  75. package/dist/services/chunkBuilder/index.js +1 -0
  76. package/dist/services/chunkBuilder/index.js.map +1 -0
  77. package/dist/services/clipGenerator/index.d.ts +30 -0
  78. package/dist/services/clipGenerator/index.d.ts.map +1 -0
  79. package/dist/services/clipGenerator/index.js +1 -0
  80. package/dist/services/clipGenerator/index.js.map +1 -0
  81. package/dist/services/clipRefiner/index.d.ts +9 -0
  82. package/dist/services/clipRefiner/index.d.ts.map +1 -0
  83. package/dist/services/clipRefiner/index.js +1 -0
  84. package/dist/services/clipRefiner/index.js.map +1 -0
  85. package/dist/services/eventDetector/index.d.ts +28 -0
  86. package/dist/services/eventDetector/index.d.ts.map +1 -0
  87. package/dist/services/eventDetector/index.js +1 -0
  88. package/dist/services/eventDetector/index.js.map +1 -0
  89. package/dist/services/llmAnalyzer/LLMAnalyzer.d.ts +41 -0
  90. package/dist/services/llmAnalyzer/LLMAnalyzer.d.ts.map +1 -0
  91. package/dist/services/llmAnalyzer/LLMAnalyzer.js +1 -0
  92. package/dist/services/llmAnalyzer/LLMAnalyzer.js.map +1 -0
  93. package/dist/services/llmAnalyzer/index.d.ts +8 -0
  94. package/dist/services/llmAnalyzer/index.d.ts.map +1 -0
  95. package/dist/services/llmAnalyzer/index.js +1 -0
  96. package/dist/services/llmAnalyzer/index.js.map +1 -0
  97. package/dist/services/metadataExtractor/index.d.ts +12 -0
  98. package/dist/services/metadataExtractor/index.d.ts.map +1 -0
  99. package/dist/services/metadataExtractor/index.js +1 -0
  100. package/dist/services/metadataExtractor/index.js.map +1 -0
  101. package/dist/services/segmentRanker/index.d.ts +3 -0
  102. package/dist/services/segmentRanker/index.d.ts.map +1 -0
  103. package/dist/services/segmentRanker/index.js +1 -0
  104. package/dist/services/segmentRanker/index.js.map +1 -0
  105. package/dist/services/signalMerger/index.d.ts +5 -0
  106. package/dist/services/signalMerger/index.d.ts.map +1 -0
  107. package/dist/services/signalMerger/index.js +1 -0
  108. package/dist/services/signalMerger/index.js.map +1 -0
  109. package/dist/services/transcriptAnalyzers/base.d.ts +25 -0
  110. package/dist/services/transcriptAnalyzers/base.d.ts.map +1 -0
  111. package/dist/services/transcriptAnalyzers/base.js +1 -0
  112. package/dist/services/transcriptAnalyzers/base.js.map +1 -0
  113. package/dist/services/transcriptAnalyzers/factory.d.ts +21 -0
  114. package/dist/services/transcriptAnalyzers/factory.d.ts.map +1 -0
  115. package/dist/services/transcriptAnalyzers/factory.js +1 -0
  116. package/dist/services/transcriptAnalyzers/factory.js.map +1 -0
  117. package/dist/services/transcriptAnalyzers/gemini.d.ts +18 -0
  118. package/dist/services/transcriptAnalyzers/gemini.d.ts.map +1 -0
  119. package/dist/services/transcriptAnalyzers/gemini.js +1 -0
  120. package/dist/services/transcriptAnalyzers/gemini.js.map +1 -0
  121. package/dist/services/transcriptAnalyzers/index.d.ts +7 -0
  122. package/dist/services/transcriptAnalyzers/index.d.ts.map +1 -0
  123. package/dist/services/transcriptAnalyzers/index.js +1 -0
  124. package/dist/services/transcriptAnalyzers/index.js.map +1 -0
  125. package/dist/services/transcriptAnalyzers/whisper.d.ts +18 -0
  126. package/dist/services/transcriptAnalyzers/whisper.d.ts.map +1 -0
  127. package/dist/services/transcriptAnalyzers/whisper.js +3 -1
  128. package/dist/services/transcriptAnalyzers/whisper.js.map +1 -0
  129. package/dist/services/transcriptAnalyzers/ytdlp.d.ts +15 -0
  130. package/dist/services/transcriptAnalyzers/ytdlp.d.ts.map +1 -0
  131. package/dist/services/transcriptAnalyzers/ytdlp.js +1 -0
  132. package/dist/services/transcriptAnalyzers/ytdlp.js.map +1 -0
  133. package/dist/services/transcriptDetector/index.d.ts +53 -0
  134. package/dist/services/transcriptDetector/index.d.ts.map +1 -0
  135. package/dist/services/transcriptDetector/index.js +1 -0
  136. package/dist/services/transcriptDetector/index.js.map +1 -0
  137. package/dist/services/transcriptFetcher/index.d.ts +26 -0
  138. package/dist/services/transcriptFetcher/index.d.ts.map +1 -0
  139. package/dist/services/transcriptFetcher/index.js +1 -0
  140. package/dist/services/transcriptFetcher/index.js.map +1 -0
  141. package/dist/services/urlParser/index.d.ts +12 -0
  142. package/dist/services/urlParser/index.d.ts.map +1 -0
  143. package/dist/services/urlParser/index.js +1 -0
  144. package/dist/services/urlParser/index.js.map +1 -0
  145. package/dist/services/videoDownloader/index.d.ts +27 -0
  146. package/dist/services/videoDownloader/index.d.ts.map +1 -0
  147. package/dist/services/videoDownloader/index.js +1 -0
  148. package/dist/services/videoDownloader/index.js.map +1 -0
  149. package/dist/types/analyzer.d.ts +21 -0
  150. package/dist/types/analyzer.d.ts.map +1 -0
  151. package/dist/types/analyzer.js +1 -0
  152. package/dist/types/analyzer.js.map +1 -0
  153. package/dist/types/audio.d.ts +26 -0
  154. package/dist/types/audio.d.ts.map +1 -0
  155. package/dist/types/audio.js +1 -0
  156. package/dist/types/audio.js.map +1 -0
  157. package/dist/types/cache.d.ts +7 -0
  158. package/dist/types/cache.d.ts.map +1 -0
  159. package/dist/types/cache.js +1 -0
  160. package/dist/types/cache.js.map +1 -0
  161. package/dist/types/cli.d.ts +23 -0
  162. package/dist/types/cli.d.ts.map +1 -0
  163. package/dist/types/cli.js +1 -0
  164. package/dist/types/cli.js.map +1 -0
  165. package/dist/types/config.d.ts +90 -0
  166. package/dist/types/config.d.ts.map +1 -0
  167. package/dist/types/config.js +1 -0
  168. package/dist/types/config.js.map +1 -0
  169. package/dist/types/downloader.d.ts +11 -0
  170. package/dist/types/downloader.d.ts.map +1 -0
  171. package/dist/types/downloader.js +1 -0
  172. package/dist/types/downloader.js.map +1 -0
  173. package/dist/types/factory.d.ts +3 -0
  174. package/dist/types/factory.d.ts.map +1 -0
  175. package/dist/types/factory.js +1 -0
  176. package/dist/types/factory.js.map +1 -0
  177. package/dist/types/index.d.ts +18 -0
  178. package/dist/types/index.d.ts.map +1 -0
  179. package/dist/types/index.js +1 -0
  180. package/dist/types/index.js.map +1 -0
  181. package/dist/types/pipeline.d.ts +48 -0
  182. package/dist/types/pipeline.d.ts.map +1 -0
  183. package/dist/types/pipeline.js +1 -0
  184. package/dist/types/pipeline.js.map +1 -0
  185. package/dist/types/segment.d.ts +42 -0
  186. package/dist/types/segment.d.ts.map +1 -0
  187. package/dist/types/segment.js +1 -0
  188. package/dist/types/segment.js.map +1 -0
  189. package/dist/types/transcript.d.ts +20 -0
  190. package/dist/types/transcript.d.ts.map +1 -0
  191. package/dist/types/transcript.js +1 -0
  192. package/dist/types/transcript.js.map +1 -0
  193. package/dist/types/video.d.ts +44 -0
  194. package/dist/types/video.d.ts.map +1 -0
  195. package/dist/types/video.js +1 -0
  196. package/dist/types/video.js.map +1 -0
  197. package/dist/utils/cache.d.ts +34 -0
  198. package/dist/utils/cache.d.ts.map +1 -0
  199. package/dist/utils/cache.js +1 -0
  200. package/dist/utils/cache.js.map +1 -0
  201. package/dist/utils/chunker.d.ts +38 -0
  202. package/dist/utils/chunker.d.ts.map +1 -0
  203. package/dist/utils/chunker.js +1 -0
  204. package/dist/utils/chunker.js.map +1 -0
  205. package/dist/utils/dumper.d.ts +12 -0
  206. package/dist/utils/dumper.d.ts.map +1 -0
  207. package/dist/utils/dumper.js +1 -0
  208. package/dist/utils/dumper.js.map +1 -0
  209. package/dist/utils/format.d.ts +6 -0
  210. package/dist/utils/format.d.ts.map +1 -0
  211. package/dist/utils/format.js +1 -0
  212. package/dist/utils/format.js.map +1 -0
  213. package/dist/utils/logger.d.ts +6 -0
  214. package/dist/utils/logger.d.ts.map +1 -0
  215. package/dist/utils/logger.js +1 -0
  216. package/dist/utils/logger.js.map +1 -0
  217. package/dist/utils/modelFactory.d.ts +14 -0
  218. package/dist/utils/modelFactory.d.ts.map +1 -0
  219. package/dist/utils/modelFactory.js +1 -0
  220. package/dist/utils/modelFactory.js.map +1 -0
  221. package/dist/utils/paths.d.ts +16 -0
  222. package/dist/utils/paths.d.ts.map +1 -0
  223. package/dist/utils/paths.js +20 -0
  224. package/dist/utils/paths.js.map +1 -0
  225. package/dist/utils/redactConfig.d.ts +7 -0
  226. package/dist/utils/redactConfig.d.ts.map +1 -0
  227. package/dist/utils/redactConfig.js +1 -0
  228. package/dist/utils/redactConfig.js.map +1 -0
  229. package/dist/utils/sliceAudio.d.ts +2 -0
  230. package/dist/utils/sliceAudio.d.ts.map +1 -0
  231. package/dist/utils/sliceAudio.js +1 -0
  232. package/dist/utils/sliceAudio.js.map +1 -0
  233. package/package.json +26 -2
  234. package/requirements.txt +5 -0
  235. package/scripts/detect_events.py +81 -0
  236. package/scripts/detect_events_whisper.py +101 -0
  237. package/scripts/transcribe_whisper.py +70 -0
  238. package/.env.example +0 -130
  239. package/CHANGELOG.md +0 -102
  240. package/CONTRIBUTING.md +0 -100
  241. package/commitlint.config.js +0 -25
@@ -0,0 +1,81 @@
1
+ import tensorflow_hub as hub
2
+ import soundfile as sf
3
+ import numpy as np
4
+ import json
5
+ import sys
6
+
7
+ GAME_EVENTS = {
8
+ 67: 'gunshot',
9
+ 366: 'explosion',
10
+ 389: 'crowd_cheering',
11
+ 63: 'gunfire_burst',
12
+ }
13
+
14
+ def cluster_events(events, gap=1.5):
15
+ if not events:
16
+ return []
17
+
18
+ events = sorted(events, key=lambda x: x['time'])
19
+ clusters = []
20
+ current_cluster = [events[0]]
21
+
22
+ for i in range(1, len(events)):
23
+ if events[i]['time'] - events[i - 1]['time'] <= gap:
24
+ current_cluster.append(events[i])
25
+ else:
26
+ max_conf = max(e['confidence'] for e in current_cluster)
27
+ first_time = current_cluster[0]['time']
28
+ clusters.append({
29
+ 'time': first_time,
30
+ 'event': current_cluster[0]['event'],
31
+ 'confidence': max_conf,
32
+ })
33
+ current_cluster = [events[i]]
34
+
35
+ if current_cluster:
36
+ max_conf = max(e['confidence'] for e in current_cluster)
37
+ first_time = current_cluster[0]['time']
38
+ clusters.append({
39
+ 'time': first_time,
40
+ 'event': current_cluster[0]['event'],
41
+ 'confidence': max_conf,
42
+ })
43
+
44
+ return clusters
45
+
46
+ def detect_events(audio_path, threshold=0.30):
47
+ model = hub.load('https://tfhub.dev/google/yamnet/1')
48
+ wav, sr = sf.read(audio_path, dtype='float32')
49
+
50
+ if sr != 16000:
51
+ import warnings
52
+ warnings.warn(f'Audio sample rate is {sr} Hz, expected 16000 Hz for YAMNet')
53
+
54
+ scores, _, _ = model(wav)
55
+ events = []
56
+
57
+ for i, frame in enumerate(scores.numpy()):
58
+ for cid, label in GAME_EVENTS.items():
59
+ if frame[cid] > threshold:
60
+ events.append({
61
+ 'time': round(i * 0.48, 2),
62
+ 'event': label,
63
+ 'confidence': float(frame[cid]),
64
+ })
65
+
66
+ return cluster_events(events, gap=1.5)
67
+
68
+ if __name__ == '__main__':
69
+ if len(sys.argv) < 2:
70
+ print(json.dumps({'error': 'Usage: python detect_events.py <audio_path> [threshold]'}))
71
+ sys.exit(1)
72
+
73
+ audio_path = sys.argv[1]
74
+ threshold = float(sys.argv[2]) if len(sys.argv) > 2 else 0.30
75
+
76
+ try:
77
+ result = detect_events(audio_path, threshold)
78
+ print(json.dumps(result))
79
+ except Exception as e:
80
+ print(json.dumps({'error': str(e)}))
81
+ sys.exit(1)
@@ -0,0 +1,101 @@
1
+ import json
2
+ import sys
3
+
4
+ # Keyword sets per game profile.
5
+ # Keys are lowercase; matches are case-insensitive.
6
+ PROFILE_KEYWORDS: dict[str, list[str]] = {
7
+ 'valorant': [
8
+ 'ace', 'clutch', 'defuse', 'spike', '1v1', '1v2', '1v3', '1v4', '1v5',
9
+ "let's go", 'no way', 'insane', 'bro', 'what', 'oh my god', 'omg',
10
+ 'unbelievable', 'crazy', 'yooo', 'yo', 'filthy', 'clean',
11
+ 'wallbang', 'headshot',
12
+ ],
13
+ 'fps': [
14
+ 'kill', 'headshot', 'streak', 'collateral', 'insane', 'no way',
15
+ "let's go", 'yooo', 'yo', 'crazy', 'oh my god', 'omg', 'unbelievable',
16
+ 'nice', 'what', 'bro',
17
+ ],
18
+ 'boss_fight': [
19
+ 'finally', "let's go", 'dead', 'down', 'phase', 'unbelievable', 'insane',
20
+ 'crazy', 'no way', 'oh my god', 'omg', 'yooo', 'yo', 'what', 'bro',
21
+ ],
22
+ 'general': [
23
+ 'insane', 'crazy', 'no way', "let's go", 'oh my god', 'omg',
24
+ 'what', 'wow', 'yooo', 'yo', 'unbelievable', 'bro',
25
+ ],
26
+ }
27
+
28
+ # Phrases that get full confidence (exact multi-word match carries more signal).
29
+ HIGH_CONFIDENCE_PHRASES: set[str] = {
30
+ 'ace', 'clutch', "let's go", 'no way', 'oh my god', 'omg', 'unbelievable',
31
+ '1v1', '1v2', '1v3', '1v4', '1v5', 'finally',
32
+ }
33
+
34
+
35
+ def score_text(text: str, keywords: list[str]) -> tuple[str | None, float]:
36
+ """
37
+ Return the first matching keyword and its confidence, or (None, 0).
38
+ Multi-word phrases and high-confidence phrases get confidence 1.0;
39
+ single-word partial matches get 0.8.
40
+ """
41
+ lower = text.lower()
42
+ for kw in keywords:
43
+ if kw in lower:
44
+ conf = 1.0 if kw in HIGH_CONFIDENCE_PHRASES else 0.8
45
+ return kw, conf
46
+ return None, 0.0
47
+
48
+
49
+ def detect_events_whisper(
50
+ audio_path: str,
51
+ model_size: str = 'medium',
52
+ game_profile: str = 'general',
53
+ threshold: float = 0.3,
54
+ ) -> list[dict]:
55
+ try:
56
+ import whisper # type: ignore
57
+ except ImportError:
58
+ print(
59
+ json.dumps({'error': 'openai-whisper not installed. Run: pip install openai-whisper'}),
60
+ file=sys.stderr,
61
+ )
62
+ sys.exit(2)
63
+
64
+ keywords = PROFILE_KEYWORDS.get(game_profile, PROFILE_KEYWORDS['general'])
65
+
66
+ model = whisper.load_model(model_size)
67
+ result = model.transcribe(audio_path, word_timestamps=False, fp16=False)
68
+
69
+ events: list[dict] = []
70
+ for seg in result.get('segments', []):
71
+ text: str = seg.get('text', '')
72
+ start: float = float(seg.get('start', 0))
73
+ matched_kw, confidence = score_text(text, keywords)
74
+ if matched_kw is not None and confidence >= threshold:
75
+ events.append({
76
+ 'time': round(start, 2),
77
+ 'event': matched_kw,
78
+ 'confidence': confidence,
79
+ })
80
+
81
+ return events
82
+
83
+
84
+ if __name__ == '__main__':
85
+ if len(sys.argv) < 2:
86
+ print(
87
+ json.dumps({'error': 'Usage: python detect_events_whisper.py <audio_path> [threshold] [game_profile] [model_size]'}),
88
+ )
89
+ sys.exit(1)
90
+
91
+ audio_path = sys.argv[1]
92
+ threshold = float(sys.argv[2]) if len(sys.argv) > 2 else 0.3
93
+ game_profile = sys.argv[3] if len(sys.argv) > 3 else 'general'
94
+ model_size = sys.argv[4] if len(sys.argv) > 4 else 'medium'
95
+
96
+ try:
97
+ result = detect_events_whisper(audio_path, model_size, game_profile, threshold)
98
+ print(json.dumps(result))
99
+ except Exception as e:
100
+ print(json.dumps({'error': str(e)}))
101
+ sys.exit(1)
@@ -0,0 +1,70 @@
1
+ """
2
+ transcribe_whisper.py — Full Whisper transcription for transcript generation.
3
+
4
+ Runs OpenAI Whisper on the provided audio file and writes a JSON array of
5
+ transcript segments to stdout:
6
+
7
+ [{"text": "...", "start": 0.0, "duration": 3.5}, ...]
8
+
9
+ Usage:
10
+ python transcribe_whisper.py <audio_path> [model_size]
11
+
12
+ Arguments:
13
+ audio_path - Path to the audio WAV file
14
+ model_size - Whisper model to use (default: medium)
15
+ Options: tiny, base, small, medium, large-v3
16
+
17
+ Requires: pip install openai-whisper
18
+ """
19
+
20
+ import json
21
+ import sys
22
+
23
+
24
+ def transcribe(audio_path: str, model_size: str = 'medium') -> list[dict]:
25
+ try:
26
+ import whisper # type: ignore
27
+ except ImportError:
28
+ print(
29
+ 'ModuleNotFoundError: openai-whisper not installed. Run: pip install openai-whisper',
30
+ file=sys.stderr,
31
+ )
32
+ sys.exit(2)
33
+
34
+ model = whisper.load_model(model_size)
35
+ result = model.transcribe(audio_path, word_timestamps=False, fp16=False)
36
+
37
+ segments: list[dict] = []
38
+ for seg in result.get('segments', []):
39
+ text: str = seg.get('text', '').strip()
40
+ start: float = float(seg.get('start', 0))
41
+ end: float = float(seg.get('end', start))
42
+ duration = max(0.0, round(end - start, 3))
43
+
44
+ if text:
45
+ segments.append({
46
+ 'text': text,
47
+ 'start': round(start, 3),
48
+ 'duration': duration,
49
+ })
50
+
51
+ return segments
52
+
53
+
54
+ if __name__ == '__main__':
55
+ if len(sys.argv) < 2:
56
+ print(
57
+ 'Usage: python transcribe_whisper.py <audio_path> [model_size]',
58
+ file=sys.stderr,
59
+ )
60
+ sys.exit(1)
61
+
62
+ audio_path = sys.argv[1]
63
+ model_size = sys.argv[2] if len(sys.argv) > 2 else 'medium'
64
+
65
+ try:
66
+ output = transcribe(audio_path, model_size)
67
+ print(json.dumps(output))
68
+ except Exception as e:
69
+ print(f'Error: {e}', file=sys.stderr)
70
+ sys.exit(1)
package/.env.example DELETED
@@ -1,130 +0,0 @@
1
- # ── Provider selection ────────────────────────────────────────────────────────
2
- # Allowed values: openai | anthropic | google | xai | mistral | groq | zai | openrouter | custom
3
- # Default: openai
4
- LLM_PROVIDER=openai
5
-
6
- # ── API keys — set only the key for your chosen provider ─────────────────────
7
- OPENAI_API_KEY=your_key_here
8
- # ANTHROPIC_API_KEY=your_key_here
9
- # GOOGLE_GENERATIVE_AI_API_KEY=your_key_here
10
- # XAI_API_KEY=your_key_here
11
- # MISTRAL_API_KEY=your_key_here
12
- # GROQ_API_KEY=your_key_here
13
- # ZAI_API_KEY=your_key_here
14
- # OPENROUTER_API_KEY=your_key_here
15
-
16
- # ── Custom OpenAI-compatible provider ─────────────────────────────────────────
17
- # Set LLM_PROVIDER=custom and supply the base URL + API key for any endpoint
18
- # that speaks the OpenAI Chat Completions API (e.g. LM Studio, vLLM, Ollama,
19
- # LocalAI, Together AI, Fireworks AI, etc.).
20
- # Both vars are required when LLM_PROVIDER=custom.
21
- # CUSTOM_OPENAI_BASE_URL=http://localhost:11434/v1
22
- # CUSTOM_OPENAI_API_KEY=your_key_here
23
-
24
- # ── Model name — must match the chosen provider's model IDs ──────────────────
25
- # openai: gpt-4o, gpt-4o-mini, gpt-4-turbo, ...
26
- # anthropic: claude-sonnet-4-5, claude-opus-4, claude-haiku-3-5, ...
27
- # google: gemini-2.0-flash, gemini-1.5-pro, ...
28
- # xai: grok-beta, grok-2, ...
29
- # mistral: mistral-large-latest, mistral-small-latest, ...
30
- # groq: llama-3.3-70b-versatile, llama-3.1-8b-instant, ...
31
- # zai: glm-5, glm-4.7, glm-4.6, glm-5-turbo, ...
32
- # openrouter: meta-llama/llama-3.3-70b-instruct:free, google/gemma-3-27b-it:free, ...
33
- # custom: depends on your endpoint (e.g. llama3.2, mistral, phi4, ...)
34
- LLM_MODEL=gpt-4o
35
-
36
- # ── Tunable parameters (all have defaults — only set if you want to override) ─
37
- # SCORE_THRESHOLD=7 # min score 1–10 to keep a segment
38
- # TOP_N_SEGMENTS=10 # max segments returned
39
- # CHUNK_LENGTH_SEC=120 # LLM chunk window size in seconds
40
- # CHUNK_OVERLAP_SEC=20 # overlap between consecutive chunks in seconds
41
- # MICRO_BLOCK_SEC=15 # micro-block grouping window in seconds
42
- # LLM_MAX_RETRIES=3 # max retries on rate-limit errors
43
- # DOWNLOAD_DIR=downloads/ # directory for yt-dlp downloads
44
- # OUTPUT_DIR=outputs/ # directory for clips and dumps
45
- # CACHE_DIR=outputs/cache # directory for transcript and LLM result caching
46
- # LLM_CONCURRENCY=3 # max parallel LLM calls
47
- # CLIP_CONCURRENCY=1 # max parallel clip generation operations
48
- # DOWNLOAD_SECTIONS_MODE=all # yt-dlp download mode: all (full video) or segments (individual clips)
49
- # FFMPEG_PRESET=fast # ffmpeg encoding preset: ultrafast, superfast, veryfast, fast (default), medium, slow, slower
50
- # TIMESTAMP_OFFSET_SECONDS=0 # Adjust all clip timestamps (positive = later, negative = earlier) if transcript is misaligned with video
51
-
52
- # ── Custom system prompt ────────────────────────────────────────────────────────
53
- # Override the default LLM system prompt with a custom one. Useful for adapting
54
- # the analysis to specific content types (e.g., comedy, tech, education).
55
- # LLM_SYSTEM_PROMPT=You are an expert video editor specializing in viral clips.
56
-
57
- # ── LLM evaluation limits ─────────────────────────────────────────────────────
58
- # Cap the number of transcript chunks sent to the LLM. Useful for testing or
59
- # controlling API costs. Unset (default) means all chunks are evaluated.
60
- # MAX_CHUNKS=5
61
-
62
- # ── Output dumping ────────────────────────────────────────────────────────────
63
- # When true (default), writes two files after each run:
64
- # OUTPUT_DIR/transcript/{videoId}.json — raw normalized transcript lines
65
- # OUTPUT_DIR/analysis/{videoId}.json — full pipeline result (metadata + ranked segments)
66
- # Set to false to disable.
67
- # DUMP_OUTPUTS=true
68
-
69
- # ── Audio event detection ───────────────────────────────────────────────────────
70
- # Enable/disable audio event detection. When false, transcript-only mode is used.
71
- # Default: true
72
- # AUDIO_DETECTION_ENABLED=true
73
-
74
- # ── Transcript provider ─────────────────────────────────────────────────────────
75
- # Comma-separated ordered fallback chain. First provider to succeed wins.
76
- # ytdlp — yt-dlp auto-generated VTT subtitles (default, no audio required)
77
- # whisper — local openai-whisper (Python), requires audio download + pip install openai-whisper
78
- # gemini — Gemini multimodal transcription (not yet implemented)
79
- # Default: ytdlp
80
- # TRANSCRIPT_PROVIDER=ytdlp
81
-
82
- # Audio provider:
83
- # gemini — Gemini Flash, semantic understanding, requires GOOGLE_GENERATIVE_AI_API_KEY
84
- # yamnet — local YAMNet (Python), class-ID based, requires: pip install tensorflow-hub soundfile numpy
85
- # whisper — local openai-whisper (Python), speech-to-text + keyword match, requires: pip install openai-whisper
86
- # both — deprecated alias for "gemini,whisper"
87
- # Default: gemini,whisper
88
- # AUDIO_PROVIDER=gemini,whisper
89
-
90
- # Whisper model size to use when AUDIO_PROVIDER=whisper or AUDIO_PROVIDER=both.
91
- # Larger models are more accurate but slower. medium is the recommended default.
92
- # Options: tiny | base | small | medium | large-v3
93
- # Default: medium
94
- # AUDIO_WHISPER_MODEL=medium
95
-
96
- # Confidence threshold (0-1). Events below this value are discarded.
97
- # For Whisper: 1.0 = exact phrase match, 0.8 = partial keyword match.
98
- # For YAMNet: raw class score from the model.
99
- # Default: 0.30
100
- # AUDIO_CONFIDENCE_THRESHOLD=0.30
101
-
102
- # Pre-roll and post-roll for audio-only clip candidates (seconds before/after event)
103
- # Default: 5s pre-roll, 15s post-roll
104
- # AUDIO_CLIP_PRE_ROLL=5
105
- # AUDIO_CLIP_POST_ROLL=15
106
-
107
- # Time window (seconds) within which audio events boost LLM segment scores
108
- # Default: 10s
109
- # AUDIO_LLM_BOOST_WINDOW=10
110
-
111
- # Score boost applied to LLM segments when audio event is detected nearby
112
- # Default: 2
113
- # AUDIO_LLM_SCORE_BOOST=2
114
-
115
- # ── Game profile ────────────────────────────────────────────────────────────────
116
- # Game-specific event detection and LLM keywords:
117
- # valorant: gunshot, gunfire_burst, explosion + ace, clutch, defuse, spike
118
- # fps: gunshot, gunfire_burst, explosion + kill, streak, headshot
119
- # boss_fight: explosion, crowd_cheering + phase, dead, finally
120
- # general: crowd_cheering, applause + insane, crazy, let's go
121
- # Default: general
122
- # GAME_PROFILE=general
123
-
124
- # ── Audio extra instructions ─────────────────────────────────────────────────
125
- # Optional extra instructions appended to the Gemini audio detection prompt,
126
- # after the game-profile preamble and before the JSON format block.
127
- # Use this to focus detection on specific moments, ignore certain sounds, or
128
- # add game-specific context not covered by the built-in profiles.
129
- # Example:
130
- # AUDIO_EXTRA_INSTRUCTIONS=Also flag moments where the caster's voice rises sharply. Ignore background music.
package/CHANGELOG.md DELETED
@@ -1,102 +0,0 @@
1
- ## [1.5.3](https://github.com/AmreetKumarkhuntia/video-clipper/compare/v1.5.2...v1.5.3) (2026-03-20)
2
-
3
- ### Bug Fixes
4
-
5
- - **release:** use GITHUB_TOKEN and project-level .npmrc for GitHub Packages ([437234f](https://github.com/AmreetKumarkhuntia/video-clipper/commit/437234fe43edbf5e7e0ad5c1e9f21149bb589bb5))
6
-
7
- ## [1.5.2](https://github.com/AmreetKumarkhuntia/video-clipper/compare/v1.5.1...v1.5.2) (2026-03-20)
8
-
9
- ### Bug Fixes
10
-
11
- - **release:** use PUSH_TOKEN for GitHub Packages auth ([eb89f64](https://github.com/AmreetKumarkhuntia/video-clipper/commit/eb89f64159f9903d5051587bb43e2e7f0c82c51a))
12
-
13
- ## [1.5.1](https://github.com/AmreetKumarkhuntia/video-clipper/compare/v1.5.0...v1.5.1) (2026-03-20)
14
-
15
- ### Bug Fixes
16
-
17
- - **release:** use explicit --registry flag for GitHub Packages publish ([1aec842](https://github.com/AmreetKumarkhuntia/video-clipper/commit/1aec842846fa1308b50eba53fee6465a0dbbb958))
18
-
19
- # [1.5.0](https://github.com/AmreetKumarkhuntia/video-clipper/compare/v1.4.0...v1.5.0) (2026-03-20)
20
-
21
- ### Features
22
-
23
- - **release:** publish to GitHub Packages alongside npm ([dee2e57](https://github.com/AmreetKumarkhuntia/video-clipper/commit/dee2e574eceb83a43be47155ad87060d74658713))
24
-
25
- # [1.4.0](https://github.com/AmreetKumarkhuntia/video-clipper/compare/v1.3.1...v1.4.0) (2026-03-20)
26
-
27
- ### Features
28
-
29
- - **commitlint:** enforce conventional commit message format ([9d99882](https://github.com/AmreetKumarkhuntia/video-clipper/commit/9d998828617be72a81fbe4e72caa15eb28e88b82))
30
-
31
- ## [1.3.1](https://github.com/AmreetKumarkhuntia/video-clipper/compare/v1.3.0...v1.3.1) (2026-03-20)
32
-
33
- ### Bug Fixes
34
-
35
- - **release:** add @semantic-release/npm plugin to update package.json version ([fe6498d](https://github.com/AmreetKumarkhuntia/video-clipper/commit/fe6498d779459455387e2ca2736862e704ea8b1c))
36
- - **release:** add @semantic-release/npm to update package.json version ([9de3397](https://github.com/AmreetKumarkhuntia/video-clipper/commit/9de33977fe9f856aa8749436cbee7422dd622322))
37
-
38
- # [1.3.0](https://github.com/AmreetKumarkhuntia/video-clipper/compare/v1.2.0...v1.3.0) (2026-03-19)
39
-
40
- ### Features
41
-
42
- - **npm:** configure npm package publishing ([b268345](https://github.com/AmreetKumarkhuntia/video-clipper/commit/b26834559ce97db683c393db765d7d5c7e989236))
43
-
44
- # [1.2.0](https://github.com/AmreetKumarkhuntia/video-clipper/compare/v1.1.1...v1.2.0) (2026-03-19)
45
-
46
- ### Features
47
-
48
- - **docs:** add advanced examples section to README with common use cases ([6d730f6](https://github.com/AmreetKumarkhuntia/video-clipper/commit/6d730f615830247977273bacb26c9bee24a6e474))
49
-
50
- ## [1.1.1](https://github.com/AmreetKumarkhuntia/video-clipper/compare/v1.1.0...v1.1.1) (2026-03-19)
51
-
52
- ### Bug Fixes
53
-
54
- - github workflows ([18a9536](https://github.com/AmreetKumarkhuntia/video-clipper/commit/18a953619ed17de71d3c9bd0a86e1b42a10aea37))
55
- - **release:** pull updated package.json before npm publish to avoid race condition ([1839e4a](https://github.com/AmreetKumarkhuntia/video-clipper/commit/1839e4a57d516234ad629f0217e4fa5f4852e4e3))
56
- - yaml correction ([33c7854](https://github.com/AmreetKumarkhuntia/video-clipper/commit/33c7854015e1266b3fc01b6206da4ec946f94307))
57
-
58
- # 1.0.0 (2026-03-18)
59
-
60
- ### Bug Fixes
61
-
62
- - **audio:** align audio chunk windows with transcript LLM chunks ([f81981f](https://github.com/AmreetKumarkhuntia/video-clipper/commit/f81981fdca8c8b4b13682112551541b1df75a138))
63
- - **audio:** fix Gemini markdown fence parse error and add per-chunk caching ([3cc237f](https://github.com/AmreetKumarkhuntia/video-clipper/commit/3cc237f851c56236fb3f2795f5378b02cf34bde3))
64
- - **audio:** fix Gemini MM.SS timestamp ambiguity and make model configurable ([2139c5f](https://github.com/AmreetKumarkhuntia/video-clipper/commit/2139c5fa2030679bb460b402108f0e64482c0d0b))
65
- - **audio:** use python3 or python whichever available ([6501bf6](https://github.com/AmreetKumarkhuntia/video-clipper/commit/6501bf6ba99ba1364d285b7b921a22ecdfd23182))
66
- - github workflows ([18a9536](https://github.com/AmreetKumarkhuntia/video-clipper/commit/18a953619ed17de71d3c9bd0a86e1b42a10aea37))
67
- - **logging:** improve chunk analysis logs and gitignore outputs dir ([cc007de](https://github.com/AmreetKumarkhuntia/video-clipper/commit/cc007de0b1f453064a907689f9f30f6da2bebded))
68
- - **release:** add explicit npm auth setup step ([21c9287](https://github.com/AmreetKumarkhuntia/video-clipper/commit/21c928748fe90f278e67e34ae57553e5c279faf2))
69
- - **release:** add explicit npm auth setup step ([7e423fa](https://github.com/AmreetKumarkhuntia/video-clipper/commit/7e423fa76779d1dd6fe0d5bd0bac1cf0d54ffc8a))
70
- - **release:** remove github release plugin, keep npm only ([b37870b](https://github.com/AmreetKumarkhuntia/video-clipper/commit/b37870bfe997ad9d16ec1e0c22d52a07c99b4aaf))
71
- - **release:** restore github releases and fix git push permissions ([73e6fea](https://github.com/AmreetKumarkhuntia/video-clipper/commit/73e6fea88040844d00cebe79953f50f5ac96bc1e))
72
- - **release:** use PUSH_TOKEN instead of GITHUB_TOKEN ([d3414ad](https://github.com/AmreetKumarkhuntia/video-clipper/commit/d3414ad84944d48bc5dfcee7562b00c358bca27e))
73
- - **tests:** add global vitest setup to mock config module ([ecffd3b](https://github.com/AmreetKumarkhuntia/video-clipper/commit/ecffd3b940b4a2a3e5d0bd29911f2e70e22bdfe8))
74
- - yaml correction ([33c7854](https://github.com/AmreetKumarkhuntia/video-clipper/commit/33c7854015e1266b3fc01b6206da4ec946f94307))
75
-
76
- ### Features
77
-
78
- - **analysis:** add per-chunk evaluations to analysis dump ([c562878](https://github.com/AmreetKumarkhuntia/video-clipper/commit/c562878971c65b8739b373cf2cac235d5c6697c0))
79
- - **audio:** add audio event result caching + requirements.txt ([5fd41d5](https://github.com/AmreetKumarkhuntia/video-clipper/commit/5fd41d5895e50060a93e9ef20621c85db52e80c4))
80
- - **audio:** add openai-whisper as local audio event detector ([12aa5be](https://github.com/AmreetKumarkhuntia/video-clipper/commit/12aa5be9a5521efef07e29f20ea93dd1a54aeda6))
81
- - **audio:** apply --max-parallel to audio event detection ([4996fc7](https://github.com/AmreetKumarkhuntia/video-clipper/commit/4996fc744f5000b791ecdd6677c95c24d5cf8e59))
82
- - **cache:** add chunk/transcript caching and format timestamps as HH:MM:SS ([4ea921d](https://github.com/AmreetKumarkhuntia/video-clipper/commit/4ea921d409c2b8cda2a1a5d59eb8bec14ca2c919))
83
- - **cache:** write chunk cache immediately after LLM analysis ([2ef3454](https://github.com/AmreetKumarkhuntia/video-clipper/commit/2ef3454a57c61abd5e0031639d6b79a726c7f413))
84
- - **ci:** add semantic-release for automated versioning ([0b7b6b6](https://github.com/AmreetKumarkhuntia/video-clipper/commit/0b7b6b65f685f486f3b51d21a78ac63108b87695))
85
- - **clip-generator:** add CLIP_CONCURRENCY config to prevent memory spikes ([be2c8af](https://github.com/AmreetKumarkhuntia/video-clipper/commit/be2c8af7763c5967b9b88d0b4406ac1a2c50af71))
86
- - **clip:** fix audio/video sync and add flexible download options ([c8ebe1a](https://github.com/AmreetKumarkhuntia/video-clipper/commit/c8ebe1a7f2218dcb881d10bd421e7b77b84009b6))
87
- - **llm:** add multi-provider support via modelFactory ([d72cb81](https://github.com/AmreetKumarkhuntia/video-clipper/commit/d72cb81d8adfe6366b60ef8ab74bbd7c38f67fe5))
88
- - **llm:** support custom system prompt via LLM_SYSTEM_PROMPT env var ([8b4525d](https://github.com/AmreetKumarkhuntia/video-clipper/commit/8b4525d9fffe2806c2232035b9a8553e56f37270))
89
- - **npm:** convert to scoped package @thunderkiller/video-clipper ([c8bf930](https://github.com/AmreetKumarkhuntia/video-clipper/commit/c8bf930c789cd9ceee3d8143fc5ff75674609f9a))
90
- - **output:** add transcript and analysis dump feature ([15db23c](https://github.com/AmreetKumarkhuntia/video-clipper/commit/15db23c77244b9db89604be463bccb97f6161987))
91
- - **phase4:** add video downloader and clip generator modules ([5c1cbde](https://github.com/AmreetKumarkhuntia/video-clipper/commit/5c1cbdeabe84a65013f7d5a8f34b3c2c336097e0))
92
- - **phase5:** add CLI flags, error handling, and progress logging ([36edf0a](https://github.com/AmreetKumarkhuntia/video-clipper/commit/36edf0a93da5389c7d5dee555fb6e3c7c7e47ee1))
93
- - **pipeline:** implement phase 2 core pipeline modules ([957b068](https://github.com/AmreetKumarkhuntia/video-clipper/commit/957b068a36dc29552c1a950ac74a8e337f097764))
94
- - **pipeline:** implement phase 3 LLM analysis and metadata modules ([8a088c9](https://github.com/AmreetKumarkhuntia/video-clipper/commit/8a088c99498824b35c9e348dd1ef8bb01eba7659))
95
- - **provider:** add custom OpenAI-compatible provider support ([5dc0c4e](https://github.com/AmreetKumarkhuntia/video-clipper/commit/5dc0c4e5fedddc97ba7907d5af89ba2fa46f2bc3))
96
- - **provider:** add openrouter support and free models doc ([e41d4a6](https://github.com/AmreetKumarkhuntia/video-clipper/commit/e41d4a6780dc5d72e83aafd1a1b4b8a46f2dd284))
97
- - **refiner:** add concurrency control and caching to segment refinement ([0738599](https://github.com/AmreetKumarkhuntia/video-clipper/commit/073859948700da0d59b806e29f03e9b7956f704d))
98
- - **release:** split into two sequential stages - GitHub then npm ([9f6d110](https://github.com/AmreetKumarkhuntia/video-clipper/commit/9f6d1108abd80cacf695d3872f4800fb84884bdd))
99
- - **scaffold:** bootstrap project structure and core infrastructure ([b187b3f](https://github.com/AmreetKumarkhuntia/video-clipper/commit/b187b3f43082ae88525f9444e84297b975167720))
100
- - **tooling:** add prettier, husky pre-commit hooks, and github actions ci ([fdbdc9a](https://github.com/AmreetKumarkhuntia/video-clipper/commit/fdbdc9a838e083d2777e6c6fb0f700b28fb1c324))
101
- - **version:** updated version ([69164f5](https://github.com/AmreetKumarkhuntia/video-clipper/commit/69164f59acca2e3236b4ab9a6e85441f89df01b4))
102
- - **yt-dlp:** add cookie support and improve error logging ([ae4afd4](https://github.com/AmreetKumarkhuntia/video-clipper/commit/ae4afd476b4176645bd69f40a62f22fd775356eb))
package/CONTRIBUTING.md DELETED
@@ -1,100 +0,0 @@
1
- # Contributing
2
-
3
- ## Getting Started
4
-
5
- ```bash
6
- pnpm install
7
- ```
8
-
9
- This will install all dependencies and set up git hooks via Husky.
10
-
11
- ## Commit Message Format
12
-
13
- This project uses [Conventional Commits](https://www.conventionalcommits.org/) enforced by [commitlint](https://commitlint.js.org/).
14
-
15
- ### Format
16
-
17
- ```
18
- <type>(<scope>): <short description>
19
-
20
- - <detail 1>
21
- - <detail 2>
22
- ```
23
-
24
- ### Types
25
-
26
- | Type | Description | Version Bump |
27
- | ---------- | ------------------------------------------------------- | ---------------------- |
28
- | `feat` | New feature | minor (1.0.0 -> 1.1.0) |
29
- | `fix` | Bug fix | patch (1.0.0 -> 1.0.1) |
30
- | `docs` | Documentation only | none |
31
- | `refactor` | Code change that neither fixes a bug nor adds a feature | none |
32
- | `test` | Adding or updating tests | none |
33
- | `chore` | Maintenance tasks (deps, config, etc.) | none |
34
- | `style` | Formatting, missing semicolons, etc. (no code change) | none |
35
- | `perf` | Performance improvement | none |
36
- | `ci` | CI/CD configuration changes | none |
37
- | `build` | Build system or external dependency changes | none |
38
- | `revert` | Reverts a previous commit | none |
39
-
40
- ### Rules
41
-
42
- - **type** (required): one from the table above
43
- - **scope** (recommended): kebab-case name of the feature area, e.g. `release`, `transcript-fetcher`, `llm-analyzer`
44
- - **short description** (required): lowercase, imperative mood, max 100 chars, no period at end
45
- - **body** (optional): pointwise with `-`, max 500 chars total
46
-
47
- ### Examples
48
-
49
- ```
50
- feat(clip-refiner): add overlap detection for adjacent segments
51
-
52
- - detect when refined segments overlap by more than 2s
53
- - merge overlapping segments and keep the higher-scored one
54
- ```
55
-
56
- ```
57
- fix(release): add @semantic-release/npm to update package.json version
58
-
59
- - add @semantic-release/npm with npmPublish: false
60
- - npm publish remains a separate workflow step
61
- ```
62
-
63
- ```
64
- docs(readme): add advanced examples section
65
- ```
66
-
67
- ### Breaking Changes
68
-
69
- Add `BREAKING CHANGE:` in the commit body or `!` after the type to trigger a major version bump:
70
-
71
- ```
72
- feat(config)!: rename ENV_VAR to NEW_ENV_VAR
73
-
74
- BREAKING CHANGE: ENV_VAR is no longer supported, use NEW_ENV_VAR instead
75
- ```
76
-
77
- ## Git Hooks
78
-
79
- The following hooks run automatically on every commit:
80
-
81
- | Hook | What it runs |
82
- | ------------ | ------------------------------------------------------------- |
83
- | `pre-commit` | `lint-staged` (Prettier), `type-check` (tsc), `test` (vitest) |
84
- | `commit-msg` | `commitlint` (validates commit message format) |
85
-
86
- If your commit is rejected, check the error output — it will tell you exactly which rule was violated.
87
-
88
- ## Code Style
89
-
90
- - TypeScript only — no plain `.js` files
91
- - Prettier handles formatting automatically via `lint-staged`
92
- - Run `pnpm format` to format all files manually
93
- - Run `pnpm type-check` to check for type errors
94
-
95
- ## Testing
96
-
97
- - Write unit tests for pure functions
98
- - Test files live in `tests/` at the project root
99
- - Run `pnpm test` to run all tests
100
- - Run `pnpm test:watch` for watch mode
@@ -1,25 +0,0 @@
1
- export default {
2
- extends: ['@commitlint/config-conventional'],
3
- rules: {
4
- 'type-enum': [
5
- 2,
6
- 'always',
7
- [
8
- 'feat',
9
- 'fix',
10
- 'docs',
11
- 'refactor',
12
- 'test',
13
- 'chore',
14
- 'style',
15
- 'perf',
16
- 'ci',
17
- 'build',
18
- 'revert',
19
- ],
20
- ],
21
- 'scope-case': [2, 'always', 'kebab-case'],
22
- 'subject-max-length': [2, 'always', 100],
23
- 'body-max-line-length': [2, 'always', 500],
24
- },
25
- };