waha-shared 1.0.319 → 1.0.324
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/dist/data/languageAssets/index.d.ts +1 -0
- package/dist/data/languageAssets/index.js +7 -0
- package/dist/data/languageAssets/languageAssets.json +44404 -0
- package/dist/data/languageAssets/languageAssets.schema.json +19 -0
- package/dist/data/languageAssets/languageAssets.zod.d.ts +3 -0
- package/dist/data/languageAssets/languageAssets.zod.js +7 -0
- package/dist/data/languages/index.d.ts +5 -1
- package/dist/data/languages/languages.json +135 -45
- package/dist/data/languages/languages.schema.json +24 -3
- package/dist/data/languages/languages.zod.d.ts +10 -1
- package/dist/data/languages/languages.zod.js +18 -5
- package/dist/data/mediaDurations/mediaDurations.json +45 -673
- package/dist/data/releaseNotes/releaseNotes.json +2 -9
- package/dist/data/youtubeVideos/youtubeVideos.json +55 -115
- package/dist/functions/ffmpeg.d.ts +104 -0
- package/dist/functions/ffmpeg.js +307 -0
- package/dist/functions/scripturePassages.js +9 -11
- package/dist/functions/sets.d.ts +15 -4
- package/dist/functions/sets.js +168 -228
- package/dist/functions/upload.d.ts +34 -0
- package/dist/functions/upload.js +49 -0
- package/dist/functions/utils.d.ts +6 -1
- package/dist/functions/utils.js +18 -4
- package/dist/types/sets.d.ts +22 -19
- package/package.json +3 -2
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VIDEO_REMOTION_OVERRIDE_ARGS = exports.VIDEO_AUDIO_ENCODE_ARGS = exports.VIDEO_ENCODE_ARGS = exports.VIDEO_ENCODE_SETTINGS = void 0;
|
|
4
|
+
exports.concatAudio = concatAudio;
|
|
5
|
+
exports.generateSilence = generateSilence;
|
|
6
|
+
exports.extractAudioSegment = extractAudioSegment;
|
|
7
|
+
exports.extractVideoAudio = extractVideoAudio;
|
|
8
|
+
exports.normalizeAudio = normalizeAudio;
|
|
9
|
+
exports.compressVideo = compressVideo;
|
|
10
|
+
exports.mixAudioWithMusic = mixAudioWithMusic;
|
|
11
|
+
exports.getDurationFromFile = getDurationFromFile;
|
|
12
|
+
const child_process_1 = require("child_process");
|
|
13
|
+
const fs_1 = require("fs");
|
|
14
|
+
const promises_1 = require("fs/promises");
|
|
15
|
+
const os_1 = require("os");
|
|
16
|
+
const path_1 = require("path");
|
|
17
|
+
function exec(cmd, args) {
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
(0, child_process_1.execFile)(cmd, args, { maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
20
|
+
if (err)
|
|
21
|
+
reject(new Error(`${cmd} failed: ${stderr}`));
|
|
22
|
+
else
|
|
23
|
+
resolve({ stdout, stderr });
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** MP3 encoder used across all ffmpeg commands. */
|
|
28
|
+
const CODEC = ['-c:a', 'libmp3lame'];
|
|
29
|
+
/**
|
|
30
|
+
* Target audio bitrate — 64 kbps is a good balance of quality and file size for
|
|
31
|
+
* speech.
|
|
32
|
+
*/
|
|
33
|
+
const BITRATE = ['-b:a', '64k'];
|
|
34
|
+
/** Sample rate — 22050 Hz is sufficient for speech content. */
|
|
35
|
+
const SAMPLE_RATE = ['-ar', '22050'];
|
|
36
|
+
/** Audio channels: stereo (2 channels). */
|
|
37
|
+
const CHANNELS = ['-ac', '2'];
|
|
38
|
+
/** Concatenate audio files using ffmpeg's concat demuxer. */
|
|
39
|
+
async function concatAudio(files, output) {
|
|
40
|
+
const dir = await (0, promises_1.mkdtemp)((0, path_1.join)((0, os_1.tmpdir)(), 'ffconcat-'));
|
|
41
|
+
const listFile = (0, path_1.join)(dir, 'list.txt');
|
|
42
|
+
await (0, promises_1.writeFile)(listFile, files.map((f) => `file '${f}'`).join('\n'));
|
|
43
|
+
await exec('ffmpeg', [
|
|
44
|
+
// Use the concat demuxer to join multiple input files
|
|
45
|
+
'-f',
|
|
46
|
+
'concat',
|
|
47
|
+
// Allow absolute paths and paths outside the working directory in the list file
|
|
48
|
+
'-safe',
|
|
49
|
+
'0',
|
|
50
|
+
// Input: the generated list file containing paths to all audio segments
|
|
51
|
+
'-i',
|
|
52
|
+
listFile,
|
|
53
|
+
// FFmpeg 8.0 introduced stricter AVFrame buffer alignment requirements for
|
|
54
|
+
// libmp3lame. The concat demuxer produces frames that don't meet this new
|
|
55
|
+
// padding requirement, causing "inadequate AVFrame plane padding" errors.
|
|
56
|
+
// aresample forces frames to be reallocated with proper alignment as a workaround.
|
|
57
|
+
'-af',
|
|
58
|
+
'aresample=resampler=swr',
|
|
59
|
+
...CODEC,
|
|
60
|
+
...BITRATE,
|
|
61
|
+
...SAMPLE_RATE,
|
|
62
|
+
...CHANNELS,
|
|
63
|
+
'-y',
|
|
64
|
+
output,
|
|
65
|
+
]);
|
|
66
|
+
}
|
|
67
|
+
/** Generate a silence MP3 of the given duration in seconds. */
|
|
68
|
+
async function generateSilence(duration, output) {
|
|
69
|
+
await exec('ffmpeg', [
|
|
70
|
+
// Use the Libavfilter virtual device as the input format
|
|
71
|
+
'-f',
|
|
72
|
+
'lavfi',
|
|
73
|
+
// Input: null audio source generating silence at 22050 Hz in stereo
|
|
74
|
+
'-i',
|
|
75
|
+
'anullsrc=r=22050:cl=stereo',
|
|
76
|
+
// Duration: stop after this many seconds
|
|
77
|
+
'-t',
|
|
78
|
+
String(duration),
|
|
79
|
+
...CODEC,
|
|
80
|
+
...BITRATE,
|
|
81
|
+
...CHANNELS,
|
|
82
|
+
'-y',
|
|
83
|
+
output,
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
/** Extract an audio segment from a file by start/end times (seconds). */
|
|
87
|
+
async function extractAudioSegment({ endTime, input, output, startTime, }) {
|
|
88
|
+
await exec('ffmpeg', [
|
|
89
|
+
// Seek to this start time (seconds) before reading input — placed before -i for fast seeking
|
|
90
|
+
'-ss',
|
|
91
|
+
String(startTime),
|
|
92
|
+
// Duration: read this many seconds of audio (endTime - startTime)
|
|
93
|
+
'-t',
|
|
94
|
+
String(endTime - startTime),
|
|
95
|
+
// Input audio file to extract from
|
|
96
|
+
'-i',
|
|
97
|
+
input,
|
|
98
|
+
// FFmpeg 8.0 workaround: force frame reallocation to satisfy libmp3lame's
|
|
99
|
+
// stricter AVFrame buffer alignment requirements (see concatAudio).
|
|
100
|
+
'-af',
|
|
101
|
+
'aresample=resampler=swr',
|
|
102
|
+
...CODEC,
|
|
103
|
+
...BITRATE,
|
|
104
|
+
...SAMPLE_RATE,
|
|
105
|
+
...CHANNELS,
|
|
106
|
+
'-y',
|
|
107
|
+
output,
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
/** Extract the audio track from a video file and re-encode to MP3. */
|
|
111
|
+
async function extractVideoAudio(input, output) {
|
|
112
|
+
await exec('ffmpeg', [
|
|
113
|
+
'-i',
|
|
114
|
+
input,
|
|
115
|
+
// Drop the video stream — we only want the audio track.
|
|
116
|
+
'-vn',
|
|
117
|
+
'-af',
|
|
118
|
+
'aresample=resampler=swr',
|
|
119
|
+
...CODEC,
|
|
120
|
+
...BITRATE,
|
|
121
|
+
...SAMPLE_RATE,
|
|
122
|
+
...CHANNELS,
|
|
123
|
+
'-y',
|
|
124
|
+
output,
|
|
125
|
+
]);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Normalize audio to EBU R128 standard using ffmpeg-normalize. Returns the path
|
|
129
|
+
* to the normalized file. Skips if already exists.
|
|
130
|
+
*/
|
|
131
|
+
async function normalizeAudio(input) {
|
|
132
|
+
const output = input.replace(/\.mp3$/, '.normalized.mp3');
|
|
133
|
+
if ((0, fs_1.existsSync)(output))
|
|
134
|
+
return output;
|
|
135
|
+
await exec('ffmpeg-normalize', [
|
|
136
|
+
// Input file to normalize
|
|
137
|
+
input,
|
|
138
|
+
// Output file path
|
|
139
|
+
'-o',
|
|
140
|
+
output,
|
|
141
|
+
// Normalization type: EBU R128 loudness standard
|
|
142
|
+
'-nt',
|
|
143
|
+
'ebu',
|
|
144
|
+
// Target integrated loudness level: -15 LUFS
|
|
145
|
+
'-t',
|
|
146
|
+
'-15',
|
|
147
|
+
// True peak ceiling: -1.0 dBTP (prevents clipping after encoding)
|
|
148
|
+
'-tp',
|
|
149
|
+
'-1.0',
|
|
150
|
+
// Loudness range target: 7.0 LU (controls dynamic range)
|
|
151
|
+
'-lrt',
|
|
152
|
+
'7.0',
|
|
153
|
+
...CODEC,
|
|
154
|
+
...SAMPLE_RATE,
|
|
155
|
+
]);
|
|
156
|
+
return output;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Source-of-truth for our standard H.264 / AAC video encode profile. Use these
|
|
160
|
+
* named values when calling APIs that take typed options (e.g. Remotion's
|
|
161
|
+
* `renderMedia`, which surfaces some flags as `codec` / `x264Preset` / `crf` /
|
|
162
|
+
* `pixelFormat` / `audioCodec` / `audioBitrate`). For direct ffmpeg shell-out,
|
|
163
|
+
* use the pre-flattened arg arrays below.
|
|
164
|
+
*
|
|
165
|
+
* The settings are tuned for concat-safe output:
|
|
166
|
+
*
|
|
167
|
+
* - `gop` of 30 puts a keyframe every 1s at 30fps, required for seekability and
|
|
168
|
+
* `-c:v copy` concat
|
|
169
|
+
* - `bFrames: 0` prevents non-monotonic DTS at concat seams
|
|
170
|
+
* - `videoTrackTimescale` of 90000 matches Remotion's default timebase
|
|
171
|
+
*/
|
|
172
|
+
exports.VIDEO_ENCODE_SETTINGS = {
|
|
173
|
+
videoCodec: 'libx264',
|
|
174
|
+
/**
|
|
175
|
+
* Value for Remotion's `codec` option (which uses short names, not ffmpeg
|
|
176
|
+
* codec ids).
|
|
177
|
+
*/
|
|
178
|
+
remotionCodec: 'h264',
|
|
179
|
+
crf: 23,
|
|
180
|
+
x264Preset: 'medium',
|
|
181
|
+
gop: 30,
|
|
182
|
+
bFrames: 0,
|
|
183
|
+
pixelFormat: 'yuv420p',
|
|
184
|
+
videoTrackTimescale: 90000,
|
|
185
|
+
audioCodec: 'aac',
|
|
186
|
+
audioSampleRate: 48000,
|
|
187
|
+
audioBitrate: '128k',
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* Ffmpeg args for H.264 video encoding (concat-safe). Includes `-movflags
|
|
191
|
+
* +faststart` so the muxed MP4 supports progressive HTTP playback.
|
|
192
|
+
*/
|
|
193
|
+
exports.VIDEO_ENCODE_ARGS = [
|
|
194
|
+
'-c:v',
|
|
195
|
+
exports.VIDEO_ENCODE_SETTINGS.videoCodec,
|
|
196
|
+
'-crf',
|
|
197
|
+
String(exports.VIDEO_ENCODE_SETTINGS.crf),
|
|
198
|
+
'-preset',
|
|
199
|
+
exports.VIDEO_ENCODE_SETTINGS.x264Preset,
|
|
200
|
+
'-g',
|
|
201
|
+
String(exports.VIDEO_ENCODE_SETTINGS.gop),
|
|
202
|
+
'-bf',
|
|
203
|
+
String(exports.VIDEO_ENCODE_SETTINGS.bFrames),
|
|
204
|
+
'-pix_fmt',
|
|
205
|
+
exports.VIDEO_ENCODE_SETTINGS.pixelFormat,
|
|
206
|
+
'-video_track_timescale',
|
|
207
|
+
String(exports.VIDEO_ENCODE_SETTINGS.videoTrackTimescale),
|
|
208
|
+
'-movflags',
|
|
209
|
+
'+faststart',
|
|
210
|
+
];
|
|
211
|
+
/** Standard AAC audio encode args to pair with {@link VIDEO_ENCODE_ARGS}. */
|
|
212
|
+
exports.VIDEO_AUDIO_ENCODE_ARGS = [
|
|
213
|
+
'-c:a',
|
|
214
|
+
exports.VIDEO_ENCODE_SETTINGS.audioCodec,
|
|
215
|
+
'-ar',
|
|
216
|
+
String(exports.VIDEO_ENCODE_SETTINGS.audioSampleRate),
|
|
217
|
+
'-b:a',
|
|
218
|
+
exports.VIDEO_ENCODE_SETTINGS.audioBitrate,
|
|
219
|
+
];
|
|
220
|
+
/**
|
|
221
|
+
* Args for use inside Remotion's `ffmpegOverride` — just the flags that
|
|
222
|
+
* `renderMedia`'s typed options don't surface natively. Currently only `-bf 0`
|
|
223
|
+
* (no B-frames), which prevents non-monotonic DTS at concat seams between
|
|
224
|
+
* Remotion-rendered clips and the B-frame-free training videos compressed by
|
|
225
|
+
* {@link compressVideo}. Pair with the typed options fed from
|
|
226
|
+
* {@link VIDEO_ENCODE_SETTINGS} for a render that matches our standard profile.
|
|
227
|
+
*
|
|
228
|
+
* Example:
|
|
229
|
+
*
|
|
230
|
+
* renderMedia({
|
|
231
|
+
* codec: VIDEO_ENCODE_SETTINGS.remotionCodec,
|
|
232
|
+
* x264Preset: VIDEO_ENCODE_SETTINGS.x264Preset,
|
|
233
|
+
* crf: VIDEO_ENCODE_SETTINGS.crf,
|
|
234
|
+
* pixelFormat: VIDEO_ENCODE_SETTINGS.pixelFormat,
|
|
235
|
+
* audioCodec: VIDEO_ENCODE_SETTINGS.audioCodec,
|
|
236
|
+
* audioBitrate: VIDEO_ENCODE_SETTINGS.audioBitrate,
|
|
237
|
+
* ffmpegOverride: ({ args }) => {
|
|
238
|
+
* const out = args[args.length - 1]
|
|
239
|
+
* const yflag = args[args.length - 2]
|
|
240
|
+
* return [...args.slice(0, -2), ...VIDEO_REMOTION_OVERRIDE_ARGS, yflag, out]
|
|
241
|
+
* },
|
|
242
|
+
* ...
|
|
243
|
+
* })
|
|
244
|
+
*/
|
|
245
|
+
exports.VIDEO_REMOTION_OVERRIDE_ARGS = [
|
|
246
|
+
'-bf',
|
|
247
|
+
String(exports.VIDEO_ENCODE_SETTINGS.bFrames),
|
|
248
|
+
];
|
|
249
|
+
/**
|
|
250
|
+
* Compress a video using the standard H.264 settings. If `audio` is provided,
|
|
251
|
+
* its track replaces the source video's audio.
|
|
252
|
+
*/
|
|
253
|
+
async function compressVideo({ input, output, audio, }) {
|
|
254
|
+
const args = ['-i', input];
|
|
255
|
+
if (audio)
|
|
256
|
+
args.push('-i', audio, '-map', '0:v:0', '-map', '1:a:0');
|
|
257
|
+
args.push(...exports.VIDEO_ENCODE_ARGS, ...exports.VIDEO_AUDIO_ENCODE_ARGS, '-y', output);
|
|
258
|
+
await exec('ffmpeg', args);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Mix narration audio with background music at a fixed volume ratio. Music is
|
|
262
|
+
* trimmed to the shorter of the two inputs; if `targetDuration` is provided,
|
|
263
|
+
* the output is padded with silence to match exactly.
|
|
264
|
+
*/
|
|
265
|
+
async function mixAudioWithMusic({ music, narration, output, targetDuration, }) {
|
|
266
|
+
const narrationDuration = await getDurationFromFile(narration);
|
|
267
|
+
const musicDuration = await getDurationFromFile(music);
|
|
268
|
+
const minDuration = Math.min(narrationDuration, musicDuration);
|
|
269
|
+
const filterComplex = [
|
|
270
|
+
`[0:a]volume=1.0[a1]`,
|
|
271
|
+
`[1:a]atrim=0:${minDuration},volume=0.1[a2]`,
|
|
272
|
+
// duration=first keeps narration length; normalize=0 preserves its volume.
|
|
273
|
+
// apad pads the mix with silence to the exact target duration.
|
|
274
|
+
`[a1][a2]amix=inputs=2:duration=first:dropout_transition=0:normalize=0,apad=whole_dur=${targetDuration}`,
|
|
275
|
+
].join(';');
|
|
276
|
+
await exec('ffmpeg', [
|
|
277
|
+
'-i',
|
|
278
|
+
narration,
|
|
279
|
+
'-i',
|
|
280
|
+
music,
|
|
281
|
+
'-filter_complex',
|
|
282
|
+
filterComplex,
|
|
283
|
+
...CODEC,
|
|
284
|
+
...BITRATE,
|
|
285
|
+
...SAMPLE_RATE,
|
|
286
|
+
...CHANNELS,
|
|
287
|
+
'-y',
|
|
288
|
+
output,
|
|
289
|
+
]);
|
|
290
|
+
}
|
|
291
|
+
/** Get the duration of an audio file in seconds using ffprobe. */
|
|
292
|
+
async function getDurationFromFile(file) {
|
|
293
|
+
const { stdout } = await exec('ffprobe', [
|
|
294
|
+
// Verbosity: only show errors (suppress informational output)
|
|
295
|
+
'-v',
|
|
296
|
+
'error',
|
|
297
|
+
// Only extract the duration field from the format section
|
|
298
|
+
'-show_entries',
|
|
299
|
+
'format=duration',
|
|
300
|
+
// Output format: plain value with no section wrappers or key name
|
|
301
|
+
'-of',
|
|
302
|
+
'default=noprint_wrappers=1:nokey=1',
|
|
303
|
+
// Input file to probe
|
|
304
|
+
file,
|
|
305
|
+
]);
|
|
306
|
+
return parseFloat(stdout.trim());
|
|
307
|
+
}
|
|
@@ -12,11 +12,11 @@ exports.enrichSections = enrichSections;
|
|
|
12
12
|
exports.normalizeVerseTimings = normalizeVerseTimings;
|
|
13
13
|
const bibleStatuses_1 = require("../data/bibleStatuses");
|
|
14
14
|
const firebase_1 = require("../data/firebase");
|
|
15
|
-
const mediaDurations_1 = require("../data/mediaDurations");
|
|
16
15
|
const languages_1 = require("../functions/languages");
|
|
17
16
|
const sets_1 = require("../types/sets");
|
|
18
17
|
const bibleBooks_1 = require("./bibleBooks");
|
|
19
18
|
const bibles_1 = require("./bibles");
|
|
19
|
+
const utils_1 = require("./utils");
|
|
20
20
|
var bibleBooks_2 = require("./bibleBooks");
|
|
21
21
|
Object.defineProperty(exports, "parseVerseRange", { enumerable: true, get: function () { return bibleBooks_2.parseVerseRange; } });
|
|
22
22
|
function getChapterUrl(params) {
|
|
@@ -211,11 +211,11 @@ function verseToSuperscript(num) {
|
|
|
211
211
|
* section has no FTB.
|
|
212
212
|
*/
|
|
213
213
|
function getFtbDuration(section, lessonInfo) {
|
|
214
|
-
if (!section.
|
|
214
|
+
if (!section.ftbPath)
|
|
215
215
|
return 0;
|
|
216
|
-
const ftbDuration =
|
|
216
|
+
const ftbDuration = (0, utils_1.getCachedDuration)(section.ftbPath);
|
|
217
217
|
if (ftbDuration == null) {
|
|
218
|
-
console.warn(`FTB duration not found for ${section.
|
|
218
|
+
console.warn(`FTB duration not found for ${(0, utils_1.basename)(section.ftbPath)}`);
|
|
219
219
|
}
|
|
220
220
|
return (ftbDuration ?? 0) + lessonInfo.lessonPauses.afterFtb;
|
|
221
221
|
}
|
|
@@ -243,7 +243,7 @@ function enrichSections(lessonInfo, scripture) {
|
|
|
243
243
|
const ignoreTimings = isStory && priorStoryLengthUnknown ? true : undefined;
|
|
244
244
|
const startTime = currentTime;
|
|
245
245
|
if (!isStory) {
|
|
246
|
-
length =
|
|
246
|
+
length = (0, utils_1.getCachedDuration)(section.path);
|
|
247
247
|
}
|
|
248
248
|
else {
|
|
249
249
|
currentTime += getFtbDuration(section, lessonInfo);
|
|
@@ -263,7 +263,7 @@ function enrichSections(lessonInfo, scripture) {
|
|
|
263
263
|
priorStoryLengthUnknown = true;
|
|
264
264
|
}
|
|
265
265
|
if (length == null) {
|
|
266
|
-
console.warn(`Missing duration for section ${section.id} (${section.chapter}, ${section.
|
|
266
|
+
console.warn(`Missing duration for section ${section.id} (${section.chapter}, ${(0, utils_1.basename)(section.path)})`);
|
|
267
267
|
}
|
|
268
268
|
currentTime += length ?? 0;
|
|
269
269
|
return { ...section, length: length ?? 0, startTime, ignoreTimings };
|
|
@@ -271,11 +271,9 @@ function enrichSections(lessonInfo, scripture) {
|
|
|
271
271
|
// Backward pass: compute application section start times from the end of the
|
|
272
272
|
// lesson file, so they are always correct even if story durations are
|
|
273
273
|
// slightly off.
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const totalDuration = mediaDurations_1.mediaDurations[lessonInfo.languageId]?.[fullFileName] ?? 0;
|
|
278
|
-
if (totalDuration > 0) {
|
|
274
|
+
const path = lessonInfo.type === 'dbs' ? lessonInfo.fullPath : lessonInfo.videoPath;
|
|
275
|
+
const totalDuration = (0, utils_1.getCachedDuration)(path);
|
|
276
|
+
if (totalDuration) {
|
|
279
277
|
let timeFromEnd = totalDuration;
|
|
280
278
|
const applicationSections = enriched.filter((s) => s.chapter === sets_1.Chapter.APPLICATION);
|
|
281
279
|
for (const section of [...applicationSections].reverse()) {
|
package/dist/functions/sets.d.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { TranslationsApp } from '../data/translationsApp/translationsApp.zod';
|
|
2
|
-
import type { LanguageInfo, MeetTranslations } from '../types/languages';
|
|
3
|
-
import { DbsInfo, EnrichedSection, Lesson, LessonInfo, SetInfo, VideoInfo } from '../types/sets';
|
|
2
|
+
import type { AppTranslations, LanguageInfo, MeetTranslations } from '../types/languages';
|
|
3
|
+
import { DbsInfo, EnrichedSection, Lesson, LessonInfo, SetInfo, VideoInfo, type Section } from '../types/sets';
|
|
4
4
|
export declare function getSetInfo({ setId, languageId, setIds, t, }: {
|
|
5
5
|
setId: string;
|
|
6
6
|
languageId: string;
|
|
7
7
|
t: MeetTranslations & TranslationsApp[string];
|
|
8
8
|
setIds: string[];
|
|
9
9
|
}): SetInfo | undefined;
|
|
10
|
+
/** Assembles all of the audio sections for a lesson. */
|
|
11
|
+
export declare function assembleLessonSections({ languageInfo, setInfo, lesson, t, useSpokenQuestions, }: {
|
|
12
|
+
languageInfo: LanguageInfo;
|
|
13
|
+
setInfo: SetInfo;
|
|
14
|
+
lesson: Lesson;
|
|
15
|
+
t: MeetTranslations & AppTranslations;
|
|
16
|
+
useSpokenQuestions: boolean | undefined;
|
|
17
|
+
}): {
|
|
18
|
+
sections: Section[];
|
|
19
|
+
fellowshipDuration: number;
|
|
20
|
+
applicationDuration: number;
|
|
21
|
+
};
|
|
10
22
|
export declare function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions, }: {
|
|
11
23
|
lessonId: Lesson['lessonId'] | undefined;
|
|
12
24
|
languageInfo: LanguageInfo;
|
|
@@ -14,9 +26,8 @@ export declare function getLessonInfo({ lessonId, languageInfo, setInfo, t, useS
|
|
|
14
26
|
t: MeetTranslations & TranslationsApp[string];
|
|
15
27
|
useSpokenQuestions?: boolean;
|
|
16
28
|
}): LessonInfo | undefined;
|
|
17
|
-
export declare function convertSToString(time: number): string;
|
|
18
29
|
/** Determines whether a lesson should be visible in Waha. */
|
|
19
|
-
export declare function shouldShowLesson(lessonInfo: LessonInfo | undefined
|
|
30
|
+
export declare function shouldShowLesson(lessonInfo: LessonInfo | undefined): boolean;
|
|
20
31
|
/**
|
|
21
32
|
* Validates that the computed lesson duration (from section lengths) matches
|
|
22
33
|
* the actual audio file duration. Sections must be enriched (all have `length`)
|