waha-shared 1.0.315 → 1.0.317
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/mediaDurations/mediaDurations.json +0 -576
- package/dist/functions/ffmpeg.d.ts +103 -0
- package/dist/functions/ffmpeg.js +305 -0
- package/dist/functions/scripturePassages.js +9 -11
- package/dist/functions/sets.d.ts +1 -1
- package/dist/functions/sets.js +80 -128
- 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 +17 -22
- package/package.json +1 -1
package/dist/functions/sets.js
CHANGED
|
@@ -6,7 +6,6 @@ exports.convertSToString = convertSToString;
|
|
|
6
6
|
exports.shouldShowLesson = shouldShowLesson;
|
|
7
7
|
exports.getExpectedLessonDuration = getExpectedLessonDuration;
|
|
8
8
|
const bibleStatuses_1 = require("../data/bibleStatuses");
|
|
9
|
-
const mediaDurations_1 = require("../data/mediaDurations");
|
|
10
9
|
const sets_1 = require("../data/sets");
|
|
11
10
|
const specialIds_1 = require("../data/specialIds");
|
|
12
11
|
const youtubeVideos_1 = require("../data/youtubeVideos");
|
|
@@ -96,52 +95,41 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
|
|
|
96
95
|
id: 'title',
|
|
97
96
|
index: 0,
|
|
98
97
|
indexWithinChapter: 0,
|
|
99
|
-
|
|
100
|
-
languageId,
|
|
98
|
+
path: '',
|
|
101
99
|
},
|
|
102
100
|
];
|
|
103
101
|
fellowshipQuestions.forEach((questionId, index) => {
|
|
104
|
-
const fileName = `${languageInfo.contentLanguages.questionAudio}.${questionId}.mp3`;
|
|
105
102
|
sections.push({
|
|
106
103
|
id: questionId,
|
|
107
|
-
languageId: languageInfo.contentLanguages.questionAudio,
|
|
108
104
|
index: sections.length,
|
|
109
105
|
header: index === 0 ? t.fellowship : undefined,
|
|
110
106
|
chapter: sets_2.Chapter.FELLOWSHIP,
|
|
111
107
|
indexWithinChapter: index,
|
|
112
108
|
text: fellowshipQuestionsText[index],
|
|
113
|
-
|
|
109
|
+
path: (0, utils_1.join)(languageInfo.contentLanguages.questionAudio, 'questions', [languageInfo.contentLanguages.questionAudio, questionId, 'mp3'].join('.')),
|
|
114
110
|
});
|
|
115
111
|
});
|
|
116
112
|
if (specialIds_1.specialIds.evaluationQuestionLessonIds.includes(lesson.lessonId)) {
|
|
117
113
|
sections.push({
|
|
118
114
|
id: 'eq',
|
|
119
|
-
languageId: languageInfo.contentLanguages.intros,
|
|
120
115
|
index: sections.length,
|
|
121
116
|
chapter: sets_2.Chapter.INTRODUCTION,
|
|
122
117
|
indexWithinChapter: 0,
|
|
123
118
|
pauseBefore: 0,
|
|
124
119
|
text: t.introductions.introductions.eq,
|
|
125
|
-
|
|
120
|
+
path: (0, utils_1.join)(languageInfo.contentLanguages.intros, [languageInfo.contentLanguages.intros, 'eq', 'mp3'].join('.')),
|
|
126
121
|
});
|
|
127
122
|
}
|
|
128
123
|
else if (specialIds_1.specialIds.growingAsDmcSetIds.includes(setInfo.setId)) {
|
|
129
|
-
const introFileName = [
|
|
130
|
-
languageInfo.contentLanguages.intros,
|
|
131
|
-
lessonId,
|
|
132
|
-
'intro',
|
|
133
|
-
'mp3',
|
|
134
|
-
].join('.');
|
|
135
124
|
sections.push({
|
|
136
125
|
id: 'introduction',
|
|
137
|
-
languageId: languageInfo.contentLanguages.intros,
|
|
138
126
|
index: sections.length,
|
|
139
127
|
chapter: sets_2.Chapter.INTRODUCTION,
|
|
140
128
|
indexWithinChapter: 0,
|
|
141
129
|
pauseBefore: languageInfo.lessonPauses.beforeFtb,
|
|
142
130
|
header: t.introductions.introduction,
|
|
143
131
|
text: t.introductions.introductions[lessonId],
|
|
144
|
-
|
|
132
|
+
path: (0, utils_1.join)(languageInfo.contentLanguages.intros, [languageInfo.contentLanguages.intros, lessonId, 'intro', 'mp3'].join('.')),
|
|
145
133
|
});
|
|
146
134
|
}
|
|
147
135
|
let lastBook = null;
|
|
@@ -155,18 +143,17 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
|
|
|
155
143
|
const ftbRequired = lastBook !== passageInfo.book;
|
|
156
144
|
sections.push({
|
|
157
145
|
id: `${bibleId};${passageId}`,
|
|
158
|
-
languageId,
|
|
159
146
|
index: sections.length,
|
|
160
147
|
chapter: sets_2.Chapter.STORY,
|
|
161
148
|
indexWithinChapter: index,
|
|
162
149
|
pauseBefore: ftbRequired
|
|
163
150
|
? languageInfo.lessonPauses.beforeFtb
|
|
164
151
|
: languageInfo.lessonPauses.betweenPassages,
|
|
165
|
-
|
|
166
|
-
?
|
|
152
|
+
ftbPath: ftbRequired
|
|
153
|
+
? (0, utils_1.join)(languageInfo.contentLanguages.ftbs, 'ftb', [languageInfo.contentLanguages.ftbs, passageInfo.book, 'mp3'].join('.'))
|
|
167
154
|
: undefined,
|
|
168
|
-
|
|
169
|
-
?
|
|
155
|
+
path: bibleStatuses_1.bibleStatuses[bibleId]?.customPassages?.[passageId]
|
|
156
|
+
? (0, utils_1.join)('audio_bibles', bibleId, 'custom_passages', [passageId, 'mp3'].join('.'))
|
|
170
157
|
: passageInfo.chapterAudioFileName,
|
|
171
158
|
header: (0, scripturePassages_1.getPassagesString)([{ bibleId, passageId }]),
|
|
172
159
|
});
|
|
@@ -191,16 +178,21 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
|
|
|
191
178
|
const ftbRequired = lastBook !== passageInfo.book;
|
|
192
179
|
sections.push({
|
|
193
180
|
id: `${modifier.bibleId};${passageId}`,
|
|
194
|
-
languageId,
|
|
195
181
|
index: sections.length,
|
|
196
182
|
chapter: sets_2.Chapter.STORY,
|
|
197
183
|
indexWithinChapter: concatStoryIndex,
|
|
198
|
-
|
|
184
|
+
path: bibleStatuses_1.bibleStatuses[modifier.bibleId]?.customPassages?.[passageId]
|
|
185
|
+
? (0, utils_1.join)('audio_bibles', modifier.bibleId, 'custom_passages', [passageId, 'mp3'].join('.'))
|
|
186
|
+
: passageInfo.chapterAudioFileName,
|
|
199
187
|
pauseBefore: ftbRequired
|
|
200
188
|
? languageInfo.lessonPauses.beforeFtb
|
|
201
189
|
: languageInfo.lessonPauses.betweenPassages,
|
|
202
|
-
|
|
203
|
-
?
|
|
190
|
+
ftbPath: ftbRequired
|
|
191
|
+
? (0, utils_1.join)(languageInfo.contentLanguages.ftbs, 'ftb', [
|
|
192
|
+
languageInfo.contentLanguages.ftbs,
|
|
193
|
+
passageInfo.book,
|
|
194
|
+
'mp3',
|
|
195
|
+
].join('.'))
|
|
204
196
|
: undefined,
|
|
205
197
|
header: (0, scripturePassages_1.getPassagesString)([{ bibleId: modifier.bibleId, passageId }]),
|
|
206
198
|
});
|
|
@@ -209,14 +201,8 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
|
|
|
209
201
|
});
|
|
210
202
|
});
|
|
211
203
|
applicationQuestions.forEach((questionId, index) => {
|
|
212
|
-
const fileName = questionId.includes('video')
|
|
213
|
-
? `${languageInfo.contentLanguages.trainingVideos}.${lessonId}.mp4`
|
|
214
|
-
: `${languageInfo.contentLanguages.questionAudio}.${questionId}.mp3`;
|
|
215
204
|
sections.push({
|
|
216
205
|
id: questionId,
|
|
217
|
-
languageId: questionId.includes('video')
|
|
218
|
-
? languageInfo.contentLanguages.trainingVideos
|
|
219
|
-
: languageInfo.contentLanguages.questionAudio,
|
|
220
206
|
index: sections.length,
|
|
221
207
|
chapter: sets_2.Chapter.APPLICATION,
|
|
222
208
|
indexWithinChapter: index,
|
|
@@ -225,7 +211,17 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
|
|
|
225
211
|
text: questionId.includes('video')
|
|
226
212
|
? t.training_video
|
|
227
213
|
: applicationQuestionsText[index],
|
|
228
|
-
|
|
214
|
+
path: questionId.includes('video')
|
|
215
|
+
? (0, utils_1.join)(languageInfo.contentLanguages.trainingVideos, 'course', 'videos_compressed', [
|
|
216
|
+
languageInfo.contentLanguages.trainingVideos,
|
|
217
|
+
lessonId,
|
|
218
|
+
'mp4',
|
|
219
|
+
].join('.'))
|
|
220
|
+
: (0, utils_1.join)(languageInfo.contentLanguages.questionAudio, 'questions', [
|
|
221
|
+
languageInfo.contentLanguages.questionAudio,
|
|
222
|
+
questionId,
|
|
223
|
+
'mp3',
|
|
224
|
+
].join('.')),
|
|
229
225
|
});
|
|
230
226
|
});
|
|
231
227
|
const baseInfo = {
|
|
@@ -241,106 +237,62 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
|
|
|
241
237
|
.filter((s) => s.chapter === sets_2.Chapter.FELLOWSHIP)
|
|
242
238
|
.reduce((sum, section) => sum +
|
|
243
239
|
(section.pauseBefore ?? 0) +
|
|
244
|
-
(
|
|
240
|
+
((0, utils_1.getCachedDuration)(section.path) ?? 0), 0);
|
|
245
241
|
let applicationDuration = sections
|
|
246
242
|
.filter((s) => s.chapter === sets_2.Chapter.APPLICATION)
|
|
247
243
|
.reduce((sum, section) => sum +
|
|
248
244
|
(section.pauseBefore ?? 0) +
|
|
249
|
-
(
|
|
245
|
+
((0, utils_1.getCachedDuration)(section.path) ?? 0), 0);
|
|
250
246
|
if (specialIds_1.specialIds.evaluationQuestionLessonIds.includes(lesson.lessonId)) {
|
|
251
|
-
const eqId = `${languageInfo.contentLanguages.intros}.eq`;
|
|
252
|
-
const eqFileName = `${eqId}.mp3`;
|
|
253
|
-
const eqPath = (0, utils_1.join)(languageInfo.contentLanguages.intros, 'introductions', eqFileName);
|
|
254
247
|
return {
|
|
255
248
|
type: 'eq',
|
|
256
249
|
...baseInfo,
|
|
257
250
|
fellowshipDuration,
|
|
258
251
|
applicationDuration,
|
|
259
|
-
|
|
260
|
-
id: eqId,
|
|
261
|
-
localFileName: eqFileName,
|
|
262
|
-
remoteFileName: eqFileName,
|
|
263
|
-
path: eqPath,
|
|
264
|
-
url: (0, utils_1.firebasePath)(eqPath),
|
|
265
|
-
},
|
|
252
|
+
eqPath: (0, utils_1.join)(languageInfo.contentLanguages.intros, 'introductions', `${languageInfo.contentLanguages.intros}.eq.mp3`),
|
|
266
253
|
};
|
|
267
254
|
}
|
|
268
255
|
const assetVersion = languageInfo.audioAssetVersion ?? '';
|
|
269
|
-
const fullLessonId = `${languageId}.${lessonId}`;
|
|
270
|
-
const fullLessonFileName = `${fullLessonId}.mp3`;
|
|
271
|
-
const fullLessonPath = (0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, fullLessonFileName);
|
|
272
|
-
const full = {
|
|
273
|
-
id: fullLessonId,
|
|
274
|
-
localFileName: fullLessonFileName,
|
|
275
|
-
remoteFileName: fullLessonFileName,
|
|
276
|
-
path: fullLessonPath,
|
|
277
|
-
url: (0, utils_1.firebasePath)(fullLessonPath),
|
|
278
|
-
};
|
|
279
|
-
const storyId = fullLessonId + '.story';
|
|
280
|
-
const storyFileName = `${storyId}.mp3`;
|
|
281
|
-
const storyPath = (0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, storyFileName);
|
|
282
|
-
const story = {
|
|
283
|
-
id: storyId,
|
|
284
|
-
localFileName: storyFileName,
|
|
285
|
-
remoteFileName: storyFileName,
|
|
286
|
-
path: storyPath,
|
|
287
|
-
url: (0, utils_1.firebasePath)(storyPath),
|
|
288
|
-
};
|
|
289
256
|
if (setInfo.setId === specialIds_1.specialIds.dmCourseSetId || languageId === 'ase') {
|
|
290
|
-
let
|
|
291
|
-
let
|
|
257
|
+
let videoPath;
|
|
258
|
+
let trainingVideoPath;
|
|
259
|
+
let trainingVideoNarrationPath;
|
|
260
|
+
let trainingVideoOriginalPath;
|
|
261
|
+
let trainingVideoNarrationWithMusicPath;
|
|
262
|
+
let youtubeLink;
|
|
292
263
|
if (languageId === 'ase') {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const aseVideoRemoteFileName = `${aseVideoLessonId}.1080.mp4`;
|
|
296
|
-
const aseVideoPath = (0, utils_1.join)(languageId, 'full_lessons', setInfo.setId, aseVideoRemoteFileName);
|
|
297
|
-
video = {
|
|
298
|
-
id: aseVideoLessonId,
|
|
299
|
-
localFileName: aseVideoLocalFileName,
|
|
300
|
-
remoteFileName: aseVideoRemoteFileName,
|
|
301
|
-
path: aseVideoPath,
|
|
302
|
-
url: (0, utils_1.firebasePath)(aseVideoPath),
|
|
303
|
-
};
|
|
304
|
-
trainingVideo = undefined;
|
|
264
|
+
videoPath = (0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, [languageId, lessonId, '1080', 'mp4'].join('.'));
|
|
265
|
+
trainingVideoPath = undefined;
|
|
305
266
|
}
|
|
306
267
|
else {
|
|
307
|
-
|
|
268
|
+
videoPath = (0, utils_1.join)(languageId, `full_lessons`, setInfo.setId, [
|
|
308
269
|
languageId,
|
|
309
270
|
languageInfo.contentLanguages.trainingVideos,
|
|
310
271
|
lessonId,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
path: videoPath,
|
|
320
|
-
url: (0, utils_1.firebasePath)(videoPath),
|
|
321
|
-
};
|
|
322
|
-
const trainingVideoId = [
|
|
323
|
-
languageInfo.contentLanguages.trainingVideos,
|
|
324
|
-
lessonId,
|
|
325
|
-
].join('.');
|
|
326
|
-
const trainingVideoFileName = `${trainingVideoId}.mp4`;
|
|
272
|
+
'1080',
|
|
273
|
+
'mp4',
|
|
274
|
+
].join('.'));
|
|
275
|
+
const trainingVideosDir = (0, utils_1.join)(languageInfo.contentLanguages.trainingVideos, 'course');
|
|
276
|
+
trainingVideoPath = (0, utils_1.join)(trainingVideosDir, 'videos_compressed', [languageInfo.contentLanguages.trainingVideos, lessonId, 'mp4'].join('.'));
|
|
277
|
+
trainingVideoNarrationPath = (0, utils_1.join)(trainingVideosDir, 'audio', [languageInfo.contentLanguages.trainingVideos, lessonId, 'mp3'].join('.'));
|
|
278
|
+
trainingVideoOriginalPath = (0, utils_1.join)(trainingVideosDir, 'videos_export', [languageInfo.contentLanguages.trainingVideos, lessonId, 'm4v'].join('.'));
|
|
279
|
+
trainingVideoNarrationWithMusicPath = (0, utils_1.join)(trainingVideosDir, 'audio_with_music', [languageInfo.contentLanguages.trainingVideos, lessonId, 'mp3'].join('.'));
|
|
327
280
|
// Add the length of the training video to the application duration.
|
|
328
|
-
applicationDuration +=
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
path: trainingVideoPath,
|
|
336
|
-
url: (0, utils_1.firebasePath)(trainingVideoPath),
|
|
337
|
-
};
|
|
281
|
+
applicationDuration += (0, utils_1.getCachedDuration)(trainingVideoPath) ?? 0;
|
|
282
|
+
if (videoPath && (0, utils_1.basename)(videoPath) in youtubeVideos_1.youtubeVideos)
|
|
283
|
+
youtubeLink =
|
|
284
|
+
'https://www.youtube.com/watch?v=' +
|
|
285
|
+
youtubeVideos_1.youtubeVideos[(0, utils_1.basename)(videoPath)];
|
|
286
|
+
if (specialIds_1.specialIds.dmCourseVideoOnlyLessonIds.includes(lessonId))
|
|
287
|
+
videoPath = trainingVideoPath;
|
|
338
288
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
289
|
+
const fullPath = (0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, [
|
|
290
|
+
languageId,
|
|
291
|
+
languageInfo.contentLanguages.trainingVideos,
|
|
292
|
+
lessonId,
|
|
293
|
+
'mp3',
|
|
294
|
+
].join('.'));
|
|
295
|
+
const storyPath = (0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, [languageId, lessonId, 'story', 'mp3'].join('.'));
|
|
344
296
|
return {
|
|
345
297
|
type: 'video',
|
|
346
298
|
...baseInfo,
|
|
@@ -348,27 +300,29 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
|
|
|
348
300
|
applicationDuration,
|
|
349
301
|
lessonSectionHeader: thisSetTranslations?.sectionHeaders?.[lessonId],
|
|
350
302
|
lessonSectionBody: thisSetTranslations?.sectionBodies?.[lessonId],
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
303
|
+
videoPath,
|
|
304
|
+
trainingVideoPath,
|
|
305
|
+
trainingVideoNarrationPath,
|
|
306
|
+
trainingVideoOriginalPath,
|
|
307
|
+
trainingVideoNarrationWithMusicPath,
|
|
308
|
+
fullPath: lesson.s.length > 0 ? fullPath : undefined,
|
|
309
|
+
storyPath: lesson.s.length > 0 ? storyPath : undefined,
|
|
355
310
|
youtubeLink,
|
|
356
311
|
passagesString,
|
|
357
312
|
};
|
|
358
313
|
}
|
|
359
314
|
else {
|
|
315
|
+
const fullPath = (0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, `${languageId}.${lessonId}.mp3`);
|
|
316
|
+
const storyPath = fullPath.replace('.mp3', '.story.mp3');
|
|
360
317
|
return {
|
|
361
318
|
type: 'dbs',
|
|
362
319
|
...baseInfo,
|
|
363
320
|
fellowshipDuration,
|
|
364
321
|
applicationDuration,
|
|
365
322
|
passagesString,
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
remoteFileName: `${languageId}.${lessonId}.1080.mp4`,
|
|
370
|
-
url: (0, utils_1.firebasePath)((0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, `${languageId}.${lessonId}.1080.mp4`)),
|
|
371
|
-
},
|
|
323
|
+
fullPath,
|
|
324
|
+
storyPath,
|
|
325
|
+
dbsCastPath: (0, utils_1.join)(languageId, `full_lessons`, setInfo.setId, [languageId, lessonId, '1080', 'mp4'].join('.')),
|
|
372
326
|
};
|
|
373
327
|
}
|
|
374
328
|
}
|
|
@@ -413,27 +367,25 @@ function convertSToString(time) {
|
|
|
413
367
|
}
|
|
414
368
|
}
|
|
415
369
|
/** Determines whether a lesson should be visible in Waha. */
|
|
416
|
-
function shouldShowLesson(lessonInfo
|
|
370
|
+
function shouldShowLesson(lessonInfo) {
|
|
417
371
|
if (lessonInfo?.lessonTitle === undefined || lessonInfo.lessonTitle === '') {
|
|
418
372
|
console.log('Missing lesson info or lessonTitle lesson:', lessonInfo?.lessonId);
|
|
419
373
|
return false;
|
|
420
374
|
}
|
|
421
375
|
else if (lessonInfo.type === 'eq' &&
|
|
422
|
-
|
|
376
|
+
(0, utils_1.getCachedDuration)(lessonInfo.eqPath) === undefined) {
|
|
423
377
|
console.log('Missing remote eq file for lesson:', lessonInfo.lessonId);
|
|
424
378
|
return false;
|
|
425
379
|
}
|
|
426
380
|
else if (lessonInfo.type === 'dbs' &&
|
|
427
|
-
|
|
428
|
-
undefined) {
|
|
381
|
+
(0, utils_1.getCachedDuration)(lessonInfo.fullPath) === undefined) {
|
|
429
382
|
console.log('Missing remote dbs file for lesson:', lessonInfo.lessonId);
|
|
430
383
|
return false;
|
|
431
384
|
}
|
|
432
385
|
else if (lessonInfo.type === 'video' &&
|
|
433
|
-
lessonInfo.
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
console.log('Missing remote video for lesson:', lessonInfo.video.remoteFileName);
|
|
386
|
+
lessonInfo.videoPath &&
|
|
387
|
+
(0, utils_1.getCachedDuration)(lessonInfo.videoPath) === undefined) {
|
|
388
|
+
console.log('Missing remote video for lesson:', lessonInfo.lessonId);
|
|
437
389
|
return false;
|
|
438
390
|
}
|
|
439
391
|
else
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal structural shape of `@google-cloud/storage`'s `Bucket`. Defined
|
|
3
|
+
* structurally so this module doesn't pull `@google-cloud/storage` into the
|
|
4
|
+
* shared package's dependency graph — callers pass the real bucket.
|
|
5
|
+
*/
|
|
6
|
+
export interface UploadBucket {
|
|
7
|
+
file(path: string): {
|
|
8
|
+
exists(): Promise<[boolean]>;
|
|
9
|
+
setMetadata(meta: {
|
|
10
|
+
metadata: Record<string, string>;
|
|
11
|
+
}): Promise<unknown>;
|
|
12
|
+
};
|
|
13
|
+
upload(localPath: string, options: {
|
|
14
|
+
destination: string;
|
|
15
|
+
contentType: string;
|
|
16
|
+
public?: boolean;
|
|
17
|
+
}): Promise<unknown>;
|
|
18
|
+
}
|
|
19
|
+
export type UploadResult = 'uploaded' | 'skipped';
|
|
20
|
+
/**
|
|
21
|
+
* Upload a local file to a Cloud Storage bucket. The content type is inferred
|
|
22
|
+
* from the file extension via {@link CONTENT_TYPE_BY_EXTENSION}. Returns
|
|
23
|
+
* `'skipped'` if the remote file already exists and `overwrite` is falsy,
|
|
24
|
+
* `'uploaded'` otherwise. Throws if the underlying bucket operation fails.
|
|
25
|
+
*
|
|
26
|
+
* Sets a `duration` metadata field (in seconds) from ffprobe when probing
|
|
27
|
+
* succeeds; metadata failures are swallowed since they're non-fatal.
|
|
28
|
+
*/
|
|
29
|
+
export declare function uploadFile({ bucket, localPath, overwrite, remotePath, }: {
|
|
30
|
+
bucket: UploadBucket;
|
|
31
|
+
localPath: string;
|
|
32
|
+
remotePath: string;
|
|
33
|
+
overwrite?: boolean;
|
|
34
|
+
}): Promise<UploadResult>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.uploadFile = uploadFile;
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
const ffmpeg_1 = require("./ffmpeg");
|
|
6
|
+
const CONTENT_TYPE_BY_EXTENSION = {
|
|
7
|
+
'.mp3': 'audio/mpeg',
|
|
8
|
+
'.mp4': 'video/mp4',
|
|
9
|
+
};
|
|
10
|
+
function contentTypeFor(path) {
|
|
11
|
+
const ext = (0, path_1.extname)(path).toLowerCase();
|
|
12
|
+
const contentType = CONTENT_TYPE_BY_EXTENSION[ext];
|
|
13
|
+
if (!contentType)
|
|
14
|
+
throw new Error(`uploadFile: no content type mapped for extension '${ext}' (path: ${path}). ` +
|
|
15
|
+
`Add it to CONTENT_TYPE_BY_EXTENSION in shared/functions/upload.ts.`);
|
|
16
|
+
return contentType;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Upload a local file to a Cloud Storage bucket. The content type is inferred
|
|
20
|
+
* from the file extension via {@link CONTENT_TYPE_BY_EXTENSION}. Returns
|
|
21
|
+
* `'skipped'` if the remote file already exists and `overwrite` is falsy,
|
|
22
|
+
* `'uploaded'` otherwise. Throws if the underlying bucket operation fails.
|
|
23
|
+
*
|
|
24
|
+
* Sets a `duration` metadata field (in seconds) from ffprobe when probing
|
|
25
|
+
* succeeds; metadata failures are swallowed since they're non-fatal.
|
|
26
|
+
*/
|
|
27
|
+
async function uploadFile({ bucket, localPath, overwrite, remotePath, }) {
|
|
28
|
+
const contentType = contentTypeFor(localPath);
|
|
29
|
+
const [exists] = await bucket.file(remotePath).exists();
|
|
30
|
+
if (exists && !overwrite)
|
|
31
|
+
return 'skipped';
|
|
32
|
+
await bucket.upload(localPath, {
|
|
33
|
+
destination: remotePath,
|
|
34
|
+
contentType,
|
|
35
|
+
public: true,
|
|
36
|
+
});
|
|
37
|
+
try {
|
|
38
|
+
const duration = await (0, ffmpeg_1.getAudioDuration)(localPath);
|
|
39
|
+
if (!isNaN(duration) && duration > 0) {
|
|
40
|
+
await bucket.file(remotePath).setMetadata({
|
|
41
|
+
metadata: { duration: duration.toString() },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Non-fatal — proceed even if setting duration metadata fails.
|
|
47
|
+
}
|
|
48
|
+
return 'uploaded';
|
|
49
|
+
}
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
export declare const join: (...parts: string[]) => string;
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const basename: (p: string) => string;
|
|
3
|
+
export declare const first: (p: string) => string;
|
|
4
|
+
export declare const dirname: (p: string) => string;
|
|
5
|
+
export declare const extname: (p: string) => string;
|
|
6
|
+
export declare const basenamenoext: (p: string) => string;
|
|
7
|
+
export declare const getCachedDuration: (path: string) => number | undefined;
|
package/dist/functions/utils.js
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const
|
|
3
|
+
exports.getCachedDuration = exports.basenamenoext = exports.extname = exports.dirname = exports.first = exports.basename = exports.join = void 0;
|
|
4
|
+
const mediaDurations_1 = require("../data/mediaDurations");
|
|
5
5
|
const join = (...parts) => parts.join('/').replace(/\/+/g, '/');
|
|
6
6
|
exports.join = join;
|
|
7
|
-
const
|
|
8
|
-
exports.
|
|
7
|
+
const basename = (p) => p.split('/').pop();
|
|
8
|
+
exports.basename = basename;
|
|
9
|
+
const first = (p) => p.split('/')[0];
|
|
10
|
+
exports.first = first;
|
|
11
|
+
const dirname = (p) => p.substring(0, p.lastIndexOf('/'));
|
|
12
|
+
exports.dirname = dirname;
|
|
13
|
+
const extname = (p) => p.slice(p.lastIndexOf('.'));
|
|
14
|
+
exports.extname = extname;
|
|
15
|
+
const basenamenoext = (p) => {
|
|
16
|
+
const base = (0, exports.basename)(p);
|
|
17
|
+
const dotIndex = base.lastIndexOf('.');
|
|
18
|
+
return dotIndex !== -1 ? base.slice(0, dotIndex) : base;
|
|
19
|
+
};
|
|
20
|
+
exports.basenamenoext = basenamenoext;
|
|
21
|
+
const getCachedDuration = (path) => mediaDurations_1.mediaDurations[(0, exports.first)(path)]?.[(0, exports.basename)(path)];
|
|
22
|
+
exports.getCachedDuration = getCachedDuration;
|
package/dist/types/sets.d.ts
CHANGED
|
@@ -13,13 +13,6 @@ export interface SetInfo extends Set {
|
|
|
13
13
|
setTag?: string;
|
|
14
14
|
setLink: string;
|
|
15
15
|
}
|
|
16
|
-
export interface Content {
|
|
17
|
-
id: string;
|
|
18
|
-
localFileName: string;
|
|
19
|
-
remoteFileName: string;
|
|
20
|
-
path: string;
|
|
21
|
-
url: string;
|
|
22
|
-
}
|
|
23
16
|
export interface BaseInfo extends LanguageInfo {
|
|
24
17
|
lessonNumber: string;
|
|
25
18
|
lessonTitle: string | undefined;
|
|
@@ -32,7 +25,6 @@ export interface BaseInfo extends LanguageInfo {
|
|
|
32
25
|
}
|
|
33
26
|
export interface Section {
|
|
34
27
|
id: string;
|
|
35
|
-
languageId: string;
|
|
36
28
|
header?: string;
|
|
37
29
|
text?: string;
|
|
38
30
|
index: number;
|
|
@@ -44,8 +36,8 @@ export interface Section {
|
|
|
44
36
|
* Filename for the "From the Book" clip to insert before this section, if
|
|
45
37
|
* there is one.
|
|
46
38
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
ftbPath?: string;
|
|
40
|
+
path: string;
|
|
49
41
|
}
|
|
50
42
|
export interface EnrichedSection extends Section {
|
|
51
43
|
startTime: number;
|
|
@@ -60,26 +52,29 @@ export interface EnrichedSection extends Section {
|
|
|
60
52
|
}
|
|
61
53
|
export interface DbsInfo extends BaseInfo, SetInfo, Lesson {
|
|
62
54
|
type: 'dbs';
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
remoteFileName: string;
|
|
67
|
-
url: string;
|
|
68
|
-
};
|
|
55
|
+
fullPath: string;
|
|
56
|
+
storyPath: string;
|
|
57
|
+
dbsCastPath: string;
|
|
69
58
|
passagesString: string;
|
|
70
59
|
}
|
|
71
60
|
export interface VideoInfo extends BaseInfo, SetInfo, Lesson {
|
|
72
61
|
type: 'video';
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
videoPath: string;
|
|
63
|
+
trainingVideoPath: string | undefined;
|
|
64
|
+
trainingVideoNarrationPath: string | undefined;
|
|
65
|
+
trainingVideoNarrationWithMusicPath: string | undefined;
|
|
66
|
+
trainingVideoOriginalPath: string | undefined;
|
|
75
67
|
/**
|
|
76
68
|
* Full audio file (training-video audio + scripture + questions). Populated
|
|
77
69
|
* only for video lessons that have audio to play (e.g. dmCourse lessons with
|
|
78
70
|
* scripture passages). Video-only lessons leave this undefined.
|
|
79
71
|
*/
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
|
|
72
|
+
fullPath?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Story audio file (scripture passages only). Same population rules as
|
|
75
|
+
* `full`.
|
|
76
|
+
*/
|
|
77
|
+
storyPath?: string;
|
|
83
78
|
lessonSectionHeader: string | undefined;
|
|
84
79
|
lessonSectionBody: string | undefined;
|
|
85
80
|
youtubeLink: string | undefined;
|
|
@@ -87,7 +82,7 @@ export interface VideoInfo extends BaseInfo, SetInfo, Lesson {
|
|
|
87
82
|
}
|
|
88
83
|
export interface EqInfo extends BaseInfo, SetInfo, Lesson {
|
|
89
84
|
type: 'eq';
|
|
90
|
-
|
|
85
|
+
eqPath: string;
|
|
91
86
|
}
|
|
92
87
|
export type LessonInfo = DbsInfo | VideoInfo | EqInfo;
|
|
93
88
|
export declare enum Chapter {
|