eprec 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/app/assets/styles.css +319 -182
- package/app/components/style-system-sample.tsx +197 -0
- package/app/styles/tokens.ts +99 -0
- package/package.json +8 -11
- package/process-course/chapter-processor.ts +6 -3
- package/process-course/cli.ts +1 -1
- package/process-course/edits/cli.ts +13 -5
- package/process-course/edits/combined-video-editor.ts +2 -2
- package/process-course/edits/edit-workspace.ts +1 -1
- package/process-course/edits/regenerate-transcript.ts +2 -2
- package/process-course/edits/remove-ranges.ts +1 -1
- package/process-course/edits/timestamp-refinement.ts +1 -1
- package/process-course/edits/transcript-output.test.ts +1 -1
- package/process-course/edits/transcript-output.ts +1 -1
- package/process-course/ffmpeg.ts +1 -1
- package/process-course/jarvis-commands/parser.test.ts +1 -1
- package/process-course/jarvis-commands/parser.ts +1 -1
- package/process-course/jarvis-commands/windows.ts +3 -3
- package/process-course/logging.ts +1 -1
- package/process-course/summary.ts +1 -1
- package/process-course/utils/filename.ts +1 -1
- package/process-course/utils/transcript.test.ts +1 -1
- package/process-course/utils/transcript.ts +1 -1
- package/{app-server.ts → src/app-server.ts} +13 -9
- package/{cli.ts → src/cli.ts} +30 -14
- package/{process-course-video.ts → src/process-course-video.ts} +9 -9
- package/{speech-detection.ts → src/speech-detection.ts} +4 -4
- package/src/utils.test.ts +71 -0
- /package/{utils.ts → src/utils.ts} +0 -0
- /package/{whispercpp-transcribe.ts → src/whispercpp-transcribe.ts} +0 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { Handle } from 'remix/component'
|
|
2
|
+
import {
|
|
3
|
+
colors,
|
|
4
|
+
mq,
|
|
5
|
+
radius,
|
|
6
|
+
responsive,
|
|
7
|
+
shadows,
|
|
8
|
+
spacing,
|
|
9
|
+
transitions,
|
|
10
|
+
typography,
|
|
11
|
+
} from '../styles/tokens.ts'
|
|
12
|
+
|
|
13
|
+
const sectionStyle = {
|
|
14
|
+
display: 'flex',
|
|
15
|
+
flexDirection: 'column',
|
|
16
|
+
gap: spacing.lg,
|
|
17
|
+
marginTop: responsive.spacingSection,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const headerStyle = {
|
|
21
|
+
display: 'flex',
|
|
22
|
+
alignItems: 'center',
|
|
23
|
+
justifyContent: 'space-between',
|
|
24
|
+
gap: spacing.md,
|
|
25
|
+
flexWrap: 'wrap',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const gridStyle = {
|
|
29
|
+
display: 'grid',
|
|
30
|
+
gap: spacing.lg,
|
|
31
|
+
gridTemplateColumns: `repeat(auto-fit, minmax(${responsive.cardMinWidth}, 1fr))`,
|
|
32
|
+
[mq.mobile]: {
|
|
33
|
+
gridTemplateColumns: '1fr',
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const cardStyle = {
|
|
38
|
+
padding: spacing.xl,
|
|
39
|
+
backgroundColor: colors.surface,
|
|
40
|
+
borderRadius: radius.lg,
|
|
41
|
+
border: `1px solid ${colors.border}`,
|
|
42
|
+
boxShadow: shadows.sm,
|
|
43
|
+
display: 'flex',
|
|
44
|
+
flexDirection: 'column',
|
|
45
|
+
gap: spacing.md,
|
|
46
|
+
transition: `box-shadow ${transitions.fast}, transform ${transitions.fast}`,
|
|
47
|
+
'&:hover': {
|
|
48
|
+
boxShadow: shadows.md,
|
|
49
|
+
transform: 'translateY(-1px)',
|
|
50
|
+
},
|
|
51
|
+
[mq.mobile]: {
|
|
52
|
+
padding: spacing.lg,
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const primaryButtonStyle = {
|
|
57
|
+
display: 'inline-flex',
|
|
58
|
+
alignItems: 'center',
|
|
59
|
+
justifyContent: 'center',
|
|
60
|
+
gap: spacing.sm,
|
|
61
|
+
padding: `${spacing.sm} ${spacing.lg}`,
|
|
62
|
+
borderRadius: radius.md,
|
|
63
|
+
border: `1px solid ${colors.primaryActive}`,
|
|
64
|
+
backgroundColor: colors.primary,
|
|
65
|
+
color: colors.onPrimary,
|
|
66
|
+
fontSize: typography.fontSize.base,
|
|
67
|
+
fontWeight: typography.fontWeight.semibold,
|
|
68
|
+
cursor: 'pointer',
|
|
69
|
+
transition: `background-color ${transitions.fast}, box-shadow ${transitions.fast}, transform ${transitions.fast}`,
|
|
70
|
+
'&:hover': {
|
|
71
|
+
backgroundColor: colors.primaryHover,
|
|
72
|
+
boxShadow: shadows.sm,
|
|
73
|
+
},
|
|
74
|
+
'&:active': {
|
|
75
|
+
backgroundColor: colors.primaryActive,
|
|
76
|
+
transform: 'translateY(1px)',
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const pillStyle = {
|
|
81
|
+
padding: `${spacing.xs} ${spacing.sm}`,
|
|
82
|
+
borderRadius: radius.pill,
|
|
83
|
+
backgroundColor: colors.infoSurface,
|
|
84
|
+
color: colors.infoText,
|
|
85
|
+
fontSize: typography.fontSize.xs,
|
|
86
|
+
fontWeight: typography.fontWeight.semibold,
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const swatchStyle = (color: string) => ({
|
|
90
|
+
width: spacing.sm,
|
|
91
|
+
height: spacing.sm,
|
|
92
|
+
borderRadius: radius.xl,
|
|
93
|
+
backgroundColor: color,
|
|
94
|
+
boxShadow: `0 0 0 1px ${colors.border}`,
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
export function StyleSystemSample(handle: Handle) {
|
|
98
|
+
return () => (
|
|
99
|
+
<section css={sectionStyle}>
|
|
100
|
+
<header css={headerStyle}>
|
|
101
|
+
<div>
|
|
102
|
+
<h2
|
|
103
|
+
css={{
|
|
104
|
+
margin: 0,
|
|
105
|
+
fontSize: typography.fontSize.xl,
|
|
106
|
+
fontWeight: typography.fontWeight.semibold,
|
|
107
|
+
color: colors.text,
|
|
108
|
+
}}
|
|
109
|
+
>
|
|
110
|
+
Design tokens
|
|
111
|
+
</h2>
|
|
112
|
+
<p
|
|
113
|
+
css={{
|
|
114
|
+
margin: 0,
|
|
115
|
+
color: colors.textMuted,
|
|
116
|
+
fontSize: typography.fontSize.base,
|
|
117
|
+
lineHeight: 1.6,
|
|
118
|
+
}}
|
|
119
|
+
>
|
|
120
|
+
Shared CSS variables and TypeScript helpers for consistent theming.
|
|
121
|
+
</p>
|
|
122
|
+
</div>
|
|
123
|
+
<span css={pillStyle}>Auto dark mode</span>
|
|
124
|
+
</header>
|
|
125
|
+
|
|
126
|
+
<div css={gridStyle}>
|
|
127
|
+
<div css={cardStyle}>
|
|
128
|
+
<h3
|
|
129
|
+
css={{
|
|
130
|
+
margin: 0,
|
|
131
|
+
fontSize: typography.fontSize.lg,
|
|
132
|
+
fontWeight: typography.fontWeight.semibold,
|
|
133
|
+
color: colors.text,
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
Surface card
|
|
137
|
+
</h3>
|
|
138
|
+
<p
|
|
139
|
+
css={{
|
|
140
|
+
margin: 0,
|
|
141
|
+
color: colors.textMuted,
|
|
142
|
+
fontSize: typography.fontSize.base,
|
|
143
|
+
lineHeight: 1.5,
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
Spacing, radius, and shadows come from tokens with responsive
|
|
147
|
+
overrides.
|
|
148
|
+
</p>
|
|
149
|
+
<button type="button" css={primaryButtonStyle}>
|
|
150
|
+
Primary action
|
|
151
|
+
</button>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<div css={cardStyle}>
|
|
155
|
+
<h3
|
|
156
|
+
css={{
|
|
157
|
+
margin: 0,
|
|
158
|
+
fontSize: typography.fontSize.lg,
|
|
159
|
+
fontWeight: typography.fontWeight.semibold,
|
|
160
|
+
color: colors.text,
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
Semantic palette
|
|
164
|
+
</h3>
|
|
165
|
+
<p
|
|
166
|
+
css={{
|
|
167
|
+
margin: 0,
|
|
168
|
+
color: colors.textMuted,
|
|
169
|
+
fontSize: typography.fontSize.base,
|
|
170
|
+
lineHeight: 1.5,
|
|
171
|
+
}}
|
|
172
|
+
>
|
|
173
|
+
Use semantic names like primary, surface, and text instead of hex
|
|
174
|
+
values.
|
|
175
|
+
</p>
|
|
176
|
+
<div
|
|
177
|
+
css={{
|
|
178
|
+
display: 'flex',
|
|
179
|
+
gap: spacing.sm,
|
|
180
|
+
alignItems: 'center',
|
|
181
|
+
flexWrap: 'wrap',
|
|
182
|
+
color: colors.textSecondary,
|
|
183
|
+
fontSize: typography.fontSize.sm,
|
|
184
|
+
}}
|
|
185
|
+
>
|
|
186
|
+
<span css={swatchStyle(colors.primary)} />
|
|
187
|
+
<span css={swatchStyle(colors.infoSurface)} />
|
|
188
|
+
<span css={swatchStyle(colors.successSurface)} />
|
|
189
|
+
<span css={swatchStyle(colors.warningSurface)} />
|
|
190
|
+
<span css={swatchStyle(colors.dangerSurface)} />
|
|
191
|
+
<span>Primary, info, success, warning, danger</span>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</section>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export const colors = {
|
|
2
|
+
primary: 'var(--color-primary)',
|
|
3
|
+
primaryHover: 'var(--color-primary-hover)',
|
|
4
|
+
primaryActive: 'var(--color-primary-active)',
|
|
5
|
+
onPrimary: 'var(--color-on-primary)',
|
|
6
|
+
background: 'var(--color-background)',
|
|
7
|
+
surface: 'var(--color-surface)',
|
|
8
|
+
surfaceMuted: 'var(--color-surface-muted)',
|
|
9
|
+
surfaceInverse: 'var(--color-surface-inverse)',
|
|
10
|
+
text: 'var(--color-text)',
|
|
11
|
+
textMuted: 'var(--color-text-muted)',
|
|
12
|
+
textSubtle: 'var(--color-text-subtle)',
|
|
13
|
+
textSecondary: 'var(--color-text-secondary)',
|
|
14
|
+
textFaint: 'var(--color-text-faint)',
|
|
15
|
+
textInverse: 'var(--color-text-inverse)',
|
|
16
|
+
border: 'var(--color-border)',
|
|
17
|
+
borderStrong: 'var(--color-border-strong)',
|
|
18
|
+
borderAccent: 'var(--color-border-accent)',
|
|
19
|
+
infoSurface: 'var(--color-info-surface)',
|
|
20
|
+
infoText: 'var(--color-info-text)',
|
|
21
|
+
successSurface: 'var(--color-success-surface)',
|
|
22
|
+
successText: 'var(--color-success-text)',
|
|
23
|
+
warningSurface: 'var(--color-warning-surface)',
|
|
24
|
+
warningText: 'var(--color-warning-text)',
|
|
25
|
+
warningBorder: 'var(--color-warning-border)',
|
|
26
|
+
dangerSurface: 'var(--color-danger-surface)',
|
|
27
|
+
dangerText: 'var(--color-danger-text)',
|
|
28
|
+
dangerBorder: 'var(--color-danger-border)',
|
|
29
|
+
dangerBorderStrong: 'var(--color-danger-border-strong)',
|
|
30
|
+
primarySoft: 'color-mix(in srgb, var(--color-primary) 12%, transparent)',
|
|
31
|
+
primaryMuted: 'color-mix(in srgb, var(--color-primary) 24%, transparent)',
|
|
32
|
+
borderSubtle: 'color-mix(in srgb, var(--color-border) 60%, transparent)',
|
|
33
|
+
} as const
|
|
34
|
+
|
|
35
|
+
export const typography = {
|
|
36
|
+
fontFamily: 'var(--font-family)',
|
|
37
|
+
fontSize: {
|
|
38
|
+
xs: 'var(--font-size-xs)',
|
|
39
|
+
sm: 'var(--font-size-sm)',
|
|
40
|
+
base: 'var(--font-size-base)',
|
|
41
|
+
lg: 'var(--font-size-lg)',
|
|
42
|
+
xl: 'var(--font-size-xl)',
|
|
43
|
+
'2xl': 'var(--font-size-2xl)',
|
|
44
|
+
},
|
|
45
|
+
fontWeight: {
|
|
46
|
+
normal: 'var(--font-weight-normal)',
|
|
47
|
+
medium: 'var(--font-weight-medium)',
|
|
48
|
+
semibold: 'var(--font-weight-semibold)',
|
|
49
|
+
bold: 'var(--font-weight-bold)',
|
|
50
|
+
},
|
|
51
|
+
} as const
|
|
52
|
+
|
|
53
|
+
export const spacing = {
|
|
54
|
+
xs: 'var(--spacing-xs)',
|
|
55
|
+
sm: 'var(--spacing-sm)',
|
|
56
|
+
md: 'var(--spacing-md)',
|
|
57
|
+
lg: 'var(--spacing-lg)',
|
|
58
|
+
xl: 'var(--spacing-xl)',
|
|
59
|
+
'2xl': 'var(--spacing-2xl)',
|
|
60
|
+
'3xl': 'var(--spacing-3xl)',
|
|
61
|
+
'4xl': 'var(--spacing-4xl)',
|
|
62
|
+
'5xl': 'var(--spacing-5xl)',
|
|
63
|
+
} as const
|
|
64
|
+
|
|
65
|
+
export const radius = {
|
|
66
|
+
sm: 'var(--radius-sm)',
|
|
67
|
+
md: 'var(--radius-md)',
|
|
68
|
+
lg: 'var(--radius-lg)',
|
|
69
|
+
xl: 'var(--radius-xl)',
|
|
70
|
+
pill: 'var(--radius-pill)',
|
|
71
|
+
} as const
|
|
72
|
+
|
|
73
|
+
export const shadows = {
|
|
74
|
+
sm: 'var(--shadow-sm)',
|
|
75
|
+
md: 'var(--shadow-md)',
|
|
76
|
+
lg: 'var(--shadow-lg)',
|
|
77
|
+
} as const
|
|
78
|
+
|
|
79
|
+
export const transitions = {
|
|
80
|
+
fast: 'var(--transition-fast)',
|
|
81
|
+
normal: 'var(--transition-normal)',
|
|
82
|
+
} as const
|
|
83
|
+
|
|
84
|
+
export const responsive = {
|
|
85
|
+
spacingPage: 'var(--spacing-page)',
|
|
86
|
+
spacingSection: 'var(--spacing-section)',
|
|
87
|
+
cardMinWidth: 'var(--card-min-width)',
|
|
88
|
+
} as const
|
|
89
|
+
|
|
90
|
+
export const breakpoints = {
|
|
91
|
+
mobile: '640px',
|
|
92
|
+
tablet: '1024px',
|
|
93
|
+
} as const
|
|
94
|
+
|
|
95
|
+
export const mq = {
|
|
96
|
+
mobile: `@media (max-width: ${breakpoints.mobile})`,
|
|
97
|
+
tablet: `@media (max-width: ${breakpoints.tablet})`,
|
|
98
|
+
desktop: `@media (min-width: ${parseInt(breakpoints.tablet) + 1}px)`,
|
|
99
|
+
} as const
|
package/package.json
CHANGED
|
@@ -1,35 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eprec",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.6.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/epicweb-dev/eprec"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"app:start": "bun --watch ./cli.ts app start",
|
|
11
|
+
"app:start": "bun --watch ./src/cli.ts app start",
|
|
12
12
|
"format": "prettier --write .",
|
|
13
|
-
"test": "bun test process-course utils.test.ts",
|
|
13
|
+
"test": "bun test process-course src/utils.test.ts",
|
|
14
14
|
"test:e2e": "bun test ./e2e",
|
|
15
|
-
"test:smoke": "bunx playwright test -c playwright-smoke-config.ts",
|
|
15
|
+
"test:smoke": "bunx playwright test -c playwright/playwright-smoke-config.ts",
|
|
16
16
|
"test:all": "bun test '**/*.test.ts'",
|
|
17
17
|
"validate": "bun run test"
|
|
18
18
|
},
|
|
19
19
|
"bin": {
|
|
20
|
-
"eprec": "./cli.ts"
|
|
20
|
+
"eprec": "./src/cli.ts"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"app/**",
|
|
24
|
-
"app-server.ts",
|
|
25
|
-
"cli.ts",
|
|
26
24
|
"process-course/**",
|
|
27
|
-
"process-course-video.ts",
|
|
28
25
|
"public/**",
|
|
29
26
|
"server/**",
|
|
30
|
-
"
|
|
31
|
-
"utils.ts",
|
|
32
|
-
"whispercpp-transcribe.ts"
|
|
27
|
+
"src/**"
|
|
33
28
|
],
|
|
34
29
|
"prettier": "@epic-web/config/prettier",
|
|
35
30
|
"devDependencies": {
|
|
@@ -43,8 +38,10 @@
|
|
|
43
38
|
"typescript": "^5"
|
|
44
39
|
},
|
|
45
40
|
"dependencies": {
|
|
41
|
+
"@inquirer/search": "^4.1.0",
|
|
46
42
|
"get-port": "^7.1.0",
|
|
47
43
|
"inquirer": "^13.2.1",
|
|
44
|
+
"match-sorter": "^8.2.0",
|
|
48
45
|
"onnxruntime-node": "^1.23.2",
|
|
49
46
|
"ora": "^9.1.0",
|
|
50
47
|
"remix": "3.0.0-alpha.0",
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
detectSpeechBounds,
|
|
4
|
+
checkSegmentHasSpeech,
|
|
5
|
+
} from '../src/speech-detection'
|
|
6
|
+
import { transcribeAudio } from '../src/whispercpp-transcribe'
|
|
7
|
+
import { clamp, formatSeconds } from '../src/utils'
|
|
5
8
|
import {
|
|
6
9
|
COMMAND_CLOSE_WORD,
|
|
7
10
|
COMMAND_WAKE_WORD,
|
package/process-course/cli.ts
CHANGED
|
@@ -2,7 +2,7 @@ import path from 'node:path'
|
|
|
2
2
|
import yargs from 'yargs/yargs'
|
|
3
3
|
import { hideBin } from 'yargs/helpers'
|
|
4
4
|
import type { Argv, Arguments } from 'yargs'
|
|
5
|
-
import { getDefaultWhisperModelPath } from '../whispercpp-transcribe'
|
|
5
|
+
import { getDefaultWhisperModelPath } from '../src/whispercpp-transcribe'
|
|
6
6
|
import { DEFAULT_MIN_CHAPTER_SECONDS, TRANSCRIPTION_PHRASES } from './config'
|
|
7
7
|
import { normalizeSkipPhrases } from './utils/transcript'
|
|
8
8
|
import { parseChapterSelection } from './utils/chapter-selection'
|
|
@@ -357,17 +357,25 @@ if (import.meta.main) {
|
|
|
357
357
|
async function promptForEditsCommand(
|
|
358
358
|
prompter: Prompter,
|
|
359
359
|
): Promise<string[] | null> {
|
|
360
|
-
const selection = await prompter.
|
|
360
|
+
const selection = await prompter.search('Choose a command (type to filter)', [
|
|
361
361
|
{
|
|
362
|
-
name: 'Edit a single video using transcript text edits',
|
|
362
|
+
name: 'edit-video - Edit a single video using transcript text edits',
|
|
363
363
|
value: 'edit-video',
|
|
364
|
+
description: 'edit-video --input <file> --transcript <json> --edited <txt>',
|
|
365
|
+
keywords: ['transcript', 'cuts', 'remove', 'trim'],
|
|
364
366
|
},
|
|
365
367
|
{
|
|
366
|
-
name: 'Combine two videos with speech-aligned padding',
|
|
368
|
+
name: 'combine-videos - Combine two videos with speech-aligned padding',
|
|
367
369
|
value: 'combine-videos',
|
|
370
|
+
description: 'combine-videos --video1 <file> --video2 <file>',
|
|
371
|
+
keywords: ['merge', 'join', 'splice', 'padding'],
|
|
368
372
|
},
|
|
369
|
-
{
|
|
370
|
-
|
|
373
|
+
{
|
|
374
|
+
name: '--help - Show help',
|
|
375
|
+
value: 'help',
|
|
376
|
+
keywords: ['usage', '--help'],
|
|
377
|
+
},
|
|
378
|
+
{ name: 'exit - Exit', value: 'exit', keywords: ['quit', 'cancel'] },
|
|
371
379
|
])
|
|
372
380
|
if (selection === 'exit') {
|
|
373
381
|
return null
|
|
@@ -4,9 +4,9 @@ import { copyFile, mkdir, mkdtemp, rename, rm } from 'node:fs/promises'
|
|
|
4
4
|
import {
|
|
5
5
|
detectSpeechBounds,
|
|
6
6
|
checkSegmentHasSpeech,
|
|
7
|
-
} from '../../speech-detection'
|
|
7
|
+
} from '../../src/speech-detection'
|
|
8
8
|
import { extractChapterSegmentAccurate, concatSegments } from '../ffmpeg'
|
|
9
|
-
import { clamp, getMediaDurationSeconds } from '../../utils'
|
|
9
|
+
import { clamp, getMediaDurationSeconds } from '../../src/utils'
|
|
10
10
|
import { EDIT_CONFIG } from '../config'
|
|
11
11
|
import { editVideo } from './video-editor'
|
|
12
12
|
import {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
import { copyFile, mkdir } from 'node:fs/promises'
|
|
3
|
-
import type { TranscriptSegment } from '../../whispercpp-transcribe'
|
|
3
|
+
import type { TranscriptSegment } from '../../src/whispercpp-transcribe'
|
|
4
4
|
import {
|
|
5
5
|
buildTranscriptWordsWithIndices,
|
|
6
6
|
generateTranscriptJson,
|
|
@@ -5,7 +5,7 @@ import { mkdtemp, readdir, rm } from 'node:fs/promises'
|
|
|
5
5
|
import yargs from 'yargs/yargs'
|
|
6
6
|
import { hideBin } from 'yargs/helpers'
|
|
7
7
|
import { extractTranscriptionAudio } from '../ffmpeg'
|
|
8
|
-
import { transcribeAudio } from '../../whispercpp-transcribe'
|
|
8
|
+
import { transcribeAudio } from '../../src/whispercpp-transcribe'
|
|
9
9
|
import { scaleTranscriptSegments } from '../jarvis-commands/parser'
|
|
10
10
|
import { EDIT_CONFIG } from '../config'
|
|
11
11
|
import {
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
generateTranscriptJson,
|
|
14
14
|
generateTranscriptText,
|
|
15
15
|
} from './transcript-output'
|
|
16
|
-
import { getMediaDurationSeconds } from '../../utils'
|
|
16
|
+
import { getMediaDurationSeconds } from '../../src/utils'
|
|
17
17
|
|
|
18
18
|
async function main() {
|
|
19
19
|
const argv = yargs(hideBin(process.argv))
|
|
@@ -6,7 +6,7 @@ import yargs from 'yargs/yargs'
|
|
|
6
6
|
import { hideBin } from 'yargs/helpers'
|
|
7
7
|
import { extractChapterSegmentAccurate, concatSegments } from '../ffmpeg'
|
|
8
8
|
import { buildKeepRanges, mergeTimeRanges } from '../utils/time-ranges'
|
|
9
|
-
import { clamp, getMediaDurationSeconds } from '../../utils'
|
|
9
|
+
import { clamp, getMediaDurationSeconds } from '../../src/utils'
|
|
10
10
|
import type { TimeRange } from '../types'
|
|
11
11
|
|
|
12
12
|
export type RemoveRangesOptions = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readAudioSamples } from '../ffmpeg'
|
|
2
2
|
import { CONFIG, EDIT_CONFIG } from '../config'
|
|
3
|
-
import { clamp } from '../../utils'
|
|
3
|
+
import { clamp } from '../../src/utils'
|
|
4
4
|
import { mergeTimeRanges } from '../utils/time-ranges'
|
|
5
5
|
import { findLowestAmplitudeBoundaryProgressive } from '../utils/audio-analysis'
|
|
6
6
|
import type { TimeRange } from '../types'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TranscriptSegment } from '../../whispercpp-transcribe'
|
|
1
|
+
import type { TranscriptSegment } from '../../src/whispercpp-transcribe'
|
|
2
2
|
import { buildTranscriptWords } from '../jarvis-commands/parser'
|
|
3
3
|
import type { TranscriptJson, TranscriptWordWithIndex } from './types'
|
|
4
4
|
|
package/process-course/ffmpeg.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
runCommand as runCommandBase,
|
|
3
3
|
runCommandBinary as runCommandBinaryBase,
|
|
4
4
|
formatSeconds,
|
|
5
|
-
} from '../utils'
|
|
5
|
+
} from '../src/utils'
|
|
6
6
|
import { CONFIG, TRANSCRIPTION_SAMPLE_RATE } from './config'
|
|
7
7
|
import { logCommand, logInfo, logWarn } from './logging'
|
|
8
8
|
import type { Chapter, LoudnormAnalysis } from './types'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { test, expect } from 'bun:test'
|
|
2
2
|
import { scaleTranscriptSegments, extractTranscriptCommands } from './parser'
|
|
3
|
-
import type { TranscriptSegment } from '../../whispercpp-transcribe'
|
|
3
|
+
import type { TranscriptSegment } from '../../src/whispercpp-transcribe'
|
|
4
4
|
|
|
5
5
|
// Factory functions for test data
|
|
6
6
|
function createSegment(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TranscriptSegment } from '../../whispercpp-transcribe'
|
|
1
|
+
import type { TranscriptSegment } from '../../src/whispercpp-transcribe'
|
|
2
2
|
import { CONFIG } from '../config'
|
|
3
3
|
import type { TimeRange } from '../types'
|
|
4
4
|
import { normalizeWords } from '../utils/transcript'
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { clamp } from '../../utils'
|
|
2
|
-
import { detectSpeechSegmentsWithVad } from '../../speech-detection'
|
|
1
|
+
import { clamp } from '../../src/utils'
|
|
2
|
+
import { detectSpeechSegmentsWithVad } from '../../src/speech-detection'
|
|
3
3
|
import { readAudioSamples } from '../ffmpeg'
|
|
4
4
|
import { CONFIG } from '../config'
|
|
5
5
|
import { logInfo } from '../logging'
|
|
6
|
-
import { formatSeconds } from '../../utils'
|
|
6
|
+
import { formatSeconds } from '../../src/utils'
|
|
7
7
|
import { mergeTimeRanges } from '../utils/time-ranges'
|
|
8
8
|
import {
|
|
9
9
|
buildSilenceGapsFromSpeech,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
normalizeWords,
|
|
8
8
|
} from './transcript'
|
|
9
9
|
import { TRANSCRIPTION_PHRASES } from '../config'
|
|
10
|
-
import type { TranscriptSegment } from '../../whispercpp-transcribe'
|
|
10
|
+
import type { TranscriptSegment } from '../../src/whispercpp-transcribe'
|
|
11
11
|
|
|
12
12
|
function createPhrases(...phrases: string[]): string[] {
|
|
13
13
|
return phrases
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TranscriptSegment } from '../../whispercpp-transcribe'
|
|
1
|
+
import type { TranscriptSegment } from '../../src/whispercpp-transcribe'
|
|
2
2
|
import type { TimeRange } from '../types'
|
|
3
3
|
import { TRANSCRIPTION_PHRASES } from '../config'
|
|
4
4
|
import { buildTranscriptWords } from '../jarvis-commands/parser'
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import '
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import '../app/config/init-env.ts'
|
|
2
3
|
|
|
3
4
|
import getPort from 'get-port'
|
|
4
|
-
import { getEnv } from '
|
|
5
|
-
import { createAppRouter } from '
|
|
6
|
-
import { createBundlingRoutes } from '
|
|
5
|
+
import { getEnv } from '../app/config/env.ts'
|
|
6
|
+
import { createAppRouter } from '../app/router.tsx'
|
|
7
|
+
import { createBundlingRoutes } from '../server/bundling.ts'
|
|
7
8
|
|
|
8
9
|
type AppServerOptions = {
|
|
9
10
|
host?: string
|
|
@@ -21,6 +22,7 @@ const SHORTCUT_COLORS: Record<string, string> = {
|
|
|
21
22
|
h: '\u001b[35m',
|
|
22
23
|
}
|
|
23
24
|
const ANSI_RESET = '\u001b[0m'
|
|
25
|
+
const APP_ROOT = path.resolve(import.meta.dirname, '..')
|
|
24
26
|
|
|
25
27
|
function colorizeShortcut(key: string) {
|
|
26
28
|
if (!COLOR_ENABLED) {
|
|
@@ -145,12 +147,12 @@ function setupShortcutHandling(options: {
|
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
function startServer(port: number, hostname: string) {
|
|
148
|
-
const router = createAppRouter(
|
|
150
|
+
const router = createAppRouter(APP_ROOT)
|
|
149
151
|
return Bun.serve({
|
|
150
152
|
port,
|
|
151
153
|
hostname,
|
|
152
154
|
idleTimeout: 30,
|
|
153
|
-
routes: createBundlingRoutes(
|
|
155
|
+
routes: createBundlingRoutes(APP_ROOT),
|
|
154
156
|
async fetch(request) {
|
|
155
157
|
try {
|
|
156
158
|
return await router.fetch(request)
|
|
@@ -214,13 +216,15 @@ export async function startAppServer(options: AppServerOptions = {}) {
|
|
|
214
216
|
console.log(`[app] running at ${url}`)
|
|
215
217
|
logShortcuts(url)
|
|
216
218
|
|
|
217
|
-
return {
|
|
218
|
-
get server() {
|
|
219
|
+
return {
|
|
220
|
+
get server() {
|
|
221
|
+
return server
|
|
222
|
+
},
|
|
219
223
|
url,
|
|
220
224
|
stop: () => {
|
|
221
225
|
cleanupInput()
|
|
222
226
|
server.stop()
|
|
223
|
-
}
|
|
227
|
+
},
|
|
224
228
|
}
|
|
225
229
|
}
|
|
226
230
|
|