waha-shared 1.0.344 → 1.0.346
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/bibleAudios/bibleAudios.json +22 -0
- package/dist/data/bibleStatuses/bibleStatuses.json +594 -159
- package/dist/data/bibleTexts/bibleTexts.json +21 -0
- 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 +148 -54
- 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 +31030 -684
- package/dist/data/releaseNotes/releaseNotes.json +16 -2
- package/dist/data/translationsApp/index.d.ts +1 -0
- package/dist/data/translationsApp/translationsApp.json +41 -0
- package/dist/data/translationsApp/translationsApp.zod.d.ts +2 -0
- package/dist/data/translationsApp/translationsApp.zod.js +1 -0
- 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 +172 -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
package/dist/functions/sets.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getSetInfo = getSetInfo;
|
|
4
|
+
exports.assembleLessonSections = assembleLessonSections;
|
|
4
5
|
exports.getLessonInfo = getLessonInfo;
|
|
5
|
-
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");
|
|
@@ -53,95 +52,79 @@ function getSetInfo({ setId, languageId, setIds, t, }) {
|
|
|
53
52
|
};
|
|
54
53
|
return toReturn;
|
|
55
54
|
}
|
|
56
|
-
function
|
|
57
|
-
if (!lessonId)
|
|
58
|
-
return;
|
|
59
|
-
else if (lessonId.split('.').length !== 3)
|
|
60
|
-
return;
|
|
61
|
-
const [category, setNumber, lessonNumber] = lessonId.split('.');
|
|
62
|
-
const languageId = languageInfo.languageId;
|
|
63
|
-
setInfo =
|
|
64
|
-
setInfo ??
|
|
65
|
-
getSetInfo({
|
|
66
|
-
setId: [category, setNumber].join('.'),
|
|
67
|
-
languageId,
|
|
68
|
-
setIds: languageInfo.setIds,
|
|
69
|
-
t,
|
|
70
|
-
});
|
|
71
|
-
if (!setInfo) {
|
|
72
|
-
console.log('Missing set info for lesson:', lessonId);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const lesson = setInfo.lessons.find((lesson) => lesson.lessonId === lessonId);
|
|
76
|
-
if (!lesson)
|
|
77
|
-
return;
|
|
78
|
-
let thisSetTranslations;
|
|
79
|
-
let lessonTitle;
|
|
55
|
+
function getSetTranslations({ setInfo, lessonId, languageId, t, }) {
|
|
80
56
|
if (languageId === 'ase') {
|
|
81
57
|
const englishEquivalent = sets_1.sets.find((set) => set.name === setInfo.name && !set.comment);
|
|
82
|
-
thisSetTranslations = t.sets.find((setTranslations) => setTranslations.setId === englishEquivalent?.setId);
|
|
83
|
-
|
|
84
|
-
|
|
58
|
+
const thisSetTranslations = t.sets.find((setTranslations) => setTranslations.setId === englishEquivalent?.setId);
|
|
59
|
+
const [, , lessonNumber] = lessonId.split('.');
|
|
60
|
+
return thisSetTranslations
|
|
61
|
+
? {
|
|
62
|
+
...thisSetTranslations,
|
|
63
|
+
lessonTitle: thisSetTranslations?.lessonTitles?.[`${englishEquivalent?.setId}.${lessonNumber}`],
|
|
64
|
+
}
|
|
65
|
+
: undefined;
|
|
85
66
|
}
|
|
86
67
|
else {
|
|
87
|
-
thisSetTranslations = t.sets.find((setTranslations) => setTranslations.setId === setInfo?.setId);
|
|
88
|
-
|
|
68
|
+
const thisSetTranslations = t.sets.find((setTranslations) => setTranslations.setId === setInfo?.setId);
|
|
69
|
+
return thisSetTranslations
|
|
70
|
+
? {
|
|
71
|
+
...thisSetTranslations,
|
|
72
|
+
lessonTitle: thisSetTranslations?.lessonTitles?.[lessonId],
|
|
73
|
+
}
|
|
74
|
+
: undefined;
|
|
89
75
|
}
|
|
76
|
+
}
|
|
77
|
+
/** Assembles all of the audio sections for a lesson. */
|
|
78
|
+
function assembleLessonSections({ languageInfo, setInfo, lesson, t, useSpokenQuestions, }) {
|
|
90
79
|
const fellowshipQuestions = languageInfo.questionSets.find((questionSet) => questionSet.questionSetId === lesson.f)?.questions ?? [];
|
|
91
|
-
const fellowshipQuestionsText = fellowshipQuestions.map((questionId) => useSpokenQuestions ? t.questionsSpoken[questionId] : t.questions[questionId]);
|
|
92
80
|
const applicationQuestions = languageInfo.questionSets.find((questionSet) => questionSet.questionSetId === lesson.a)?.questions ?? [];
|
|
81
|
+
const fellowshipQuestionsText = fellowshipQuestions.map((questionId) => useSpokenQuestions ? t.questionsSpoken[questionId] : t.questions[questionId]);
|
|
93
82
|
const applicationQuestionsText = applicationQuestions.map((questionId) => useSpokenQuestions ? t.questionsSpoken[questionId] : t.questions[questionId]);
|
|
94
83
|
const sections = [
|
|
95
84
|
{
|
|
96
85
|
id: 'title',
|
|
97
86
|
index: 0,
|
|
98
87
|
indexWithinChapter: 0,
|
|
99
|
-
|
|
100
|
-
languageId,
|
|
88
|
+
path: '',
|
|
101
89
|
},
|
|
102
90
|
];
|
|
103
91
|
fellowshipQuestions.forEach((questionId, index) => {
|
|
104
|
-
const fileName = `${languageInfo.contentLanguages.questionAudio}.${questionId}.mp3`;
|
|
105
92
|
sections.push({
|
|
106
93
|
id: questionId,
|
|
107
|
-
languageId: languageInfo.contentLanguages.questionAudio,
|
|
108
94
|
index: sections.length,
|
|
109
95
|
header: index === 0 ? t.fellowship : undefined,
|
|
110
96
|
chapter: sets_2.Chapter.FELLOWSHIP,
|
|
111
97
|
indexWithinChapter: index,
|
|
112
98
|
text: fellowshipQuestionsText[index],
|
|
113
|
-
|
|
99
|
+
path: (0, utils_1.join)(languageInfo.contentLanguages.questionAudio, 'questions', [languageInfo.contentLanguages.questionAudio, questionId, 'mp3'].join('.')),
|
|
114
100
|
});
|
|
115
101
|
});
|
|
116
102
|
if (specialIds_1.specialIds.evaluationQuestionLessonIds.includes(lesson.lessonId)) {
|
|
117
103
|
sections.push({
|
|
118
104
|
id: 'eq',
|
|
119
|
-
languageId: languageInfo.contentLanguages.intros,
|
|
120
105
|
index: sections.length,
|
|
121
106
|
chapter: sets_2.Chapter.INTRODUCTION,
|
|
122
107
|
indexWithinChapter: 0,
|
|
123
108
|
pauseBefore: 0,
|
|
124
109
|
text: t.introductions.introductions.eq,
|
|
125
|
-
|
|
110
|
+
path: (0, utils_1.join)(languageInfo.contentLanguages.intros, [languageInfo.contentLanguages.intros, 'eq', 'mp3'].join('.')),
|
|
126
111
|
});
|
|
127
112
|
}
|
|
128
113
|
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
114
|
sections.push({
|
|
136
115
|
id: 'introduction',
|
|
137
|
-
languageId: languageInfo.contentLanguages.intros,
|
|
138
116
|
index: sections.length,
|
|
139
117
|
chapter: sets_2.Chapter.INTRODUCTION,
|
|
140
118
|
indexWithinChapter: 0,
|
|
141
119
|
pauseBefore: languageInfo.lessonPauses.beforeFtb,
|
|
142
120
|
header: t.introductions.introduction,
|
|
143
|
-
text: t.introductions.introductions[lessonId],
|
|
144
|
-
|
|
121
|
+
text: t.introductions.introductions[lesson.lessonId],
|
|
122
|
+
path: (0, utils_1.join)(languageInfo.contentLanguages.intros, [
|
|
123
|
+
languageInfo.contentLanguages.intros,
|
|
124
|
+
lesson.lessonId,
|
|
125
|
+
'intro',
|
|
126
|
+
'mp3',
|
|
127
|
+
].join('.')),
|
|
145
128
|
});
|
|
146
129
|
}
|
|
147
130
|
let lastBook = null;
|
|
@@ -155,29 +138,22 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
|
|
|
155
138
|
const ftbRequired = lastBook !== passageInfo.book;
|
|
156
139
|
sections.push({
|
|
157
140
|
id: `${bibleId};${passageId}`,
|
|
158
|
-
languageId,
|
|
159
141
|
index: sections.length,
|
|
160
142
|
chapter: sets_2.Chapter.STORY,
|
|
161
143
|
indexWithinChapter: index,
|
|
162
144
|
pauseBefore: ftbRequired
|
|
163
145
|
? languageInfo.lessonPauses.beforeFtb
|
|
164
146
|
: languageInfo.lessonPauses.betweenPassages,
|
|
165
|
-
|
|
166
|
-
?
|
|
147
|
+
ftbPath: ftbRequired
|
|
148
|
+
? (0, utils_1.join)(languageInfo.contentLanguages.ftbs, 'ftb', [languageInfo.contentLanguages.ftbs, passageInfo.book, 'mp3'].join('.'))
|
|
167
149
|
: undefined,
|
|
168
|
-
|
|
169
|
-
?
|
|
150
|
+
path: bibleStatuses_1.bibleStatuses[bibleId]?.customPassages?.[passageId]
|
|
151
|
+
? (0, utils_1.join)('audio_bibles', bibleId, 'custom_passages', [passageId, 'mp3'].join('.'))
|
|
170
152
|
: passageInfo.chapterAudioFileName,
|
|
171
153
|
header: (0, scripturePassages_1.getPassagesString)([{ bibleId, passageId }]),
|
|
172
154
|
});
|
|
173
155
|
lastBook = passageInfo.book;
|
|
174
156
|
});
|
|
175
|
-
const passagesString = (0, scripturePassages_1.getPassagesString)(sections
|
|
176
|
-
.filter((section) => section.chapter === sets_2.Chapter.STORY)
|
|
177
|
-
.map((section) => {
|
|
178
|
-
const [bibleId, passageId] = section.id.split(';');
|
|
179
|
-
return { passageId, bibleId };
|
|
180
|
-
}));
|
|
181
157
|
let concatStoryIndex = lesson.s.length;
|
|
182
158
|
languageInfo.bibleModifiers?.forEach((modifier) => {
|
|
183
159
|
lesson.s.forEach((passageId) => {
|
|
@@ -191,16 +167,21 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
|
|
|
191
167
|
const ftbRequired = lastBook !== passageInfo.book;
|
|
192
168
|
sections.push({
|
|
193
169
|
id: `${modifier.bibleId};${passageId}`,
|
|
194
|
-
languageId,
|
|
195
170
|
index: sections.length,
|
|
196
171
|
chapter: sets_2.Chapter.STORY,
|
|
197
172
|
indexWithinChapter: concatStoryIndex,
|
|
198
|
-
|
|
173
|
+
path: bibleStatuses_1.bibleStatuses[modifier.bibleId]?.customPassages?.[passageId]
|
|
174
|
+
? (0, utils_1.join)('audio_bibles', modifier.bibleId, 'custom_passages', [passageId, 'mp3'].join('.'))
|
|
175
|
+
: passageInfo.chapterAudioFileName,
|
|
199
176
|
pauseBefore: ftbRequired
|
|
200
177
|
? languageInfo.lessonPauses.beforeFtb
|
|
201
178
|
: languageInfo.lessonPauses.betweenPassages,
|
|
202
|
-
|
|
203
|
-
?
|
|
179
|
+
ftbPath: ftbRequired
|
|
180
|
+
? (0, utils_1.join)(languageInfo.contentLanguages.ftbs, 'ftb', [
|
|
181
|
+
languageInfo.contentLanguages.ftbs,
|
|
182
|
+
passageInfo.book,
|
|
183
|
+
'mp3',
|
|
184
|
+
].join('.'))
|
|
204
185
|
: undefined,
|
|
205
186
|
header: (0, scripturePassages_1.getPassagesString)([{ bibleId: modifier.bibleId, passageId }]),
|
|
206
187
|
});
|
|
@@ -209,14 +190,8 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
|
|
|
209
190
|
});
|
|
210
191
|
});
|
|
211
192
|
applicationQuestions.forEach((questionId, index) => {
|
|
212
|
-
const fileName = questionId.includes('video')
|
|
213
|
-
? `${languageInfo.contentLanguages.trainingVideos}.${lessonId}.mp4`
|
|
214
|
-
: `${languageInfo.contentLanguages.questionAudio}.${questionId}.mp3`;
|
|
215
193
|
sections.push({
|
|
216
194
|
id: questionId,
|
|
217
|
-
languageId: questionId.includes('video')
|
|
218
|
-
? languageInfo.contentLanguages.trainingVideos
|
|
219
|
-
: languageInfo.contentLanguages.questionAudio,
|
|
220
195
|
index: sections.length,
|
|
221
196
|
chapter: sets_2.Chapter.APPLICATION,
|
|
222
197
|
indexWithinChapter: index,
|
|
@@ -225,212 +200,181 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
|
|
|
225
200
|
text: questionId.includes('video')
|
|
226
201
|
? t.training_video
|
|
227
202
|
: applicationQuestionsText[index],
|
|
228
|
-
|
|
203
|
+
path: questionId.includes('video')
|
|
204
|
+
? (0, utils_1.join)(languageInfo.contentLanguages.trainingVideos, 'course', 'videos_compressed', [
|
|
205
|
+
languageInfo.contentLanguages.trainingVideos,
|
|
206
|
+
lesson.lessonId,
|
|
207
|
+
'mp4',
|
|
208
|
+
].join('.'))
|
|
209
|
+
: (0, utils_1.join)(languageInfo.contentLanguages.questionAudio, 'questions', [
|
|
210
|
+
languageInfo.contentLanguages.questionAudio,
|
|
211
|
+
questionId,
|
|
212
|
+
'mp3',
|
|
213
|
+
].join('.')),
|
|
229
214
|
});
|
|
230
215
|
});
|
|
216
|
+
return {
|
|
217
|
+
sections,
|
|
218
|
+
fellowshipDuration: sections
|
|
219
|
+
.filter((s) => s.chapter === sets_2.Chapter.FELLOWSHIP)
|
|
220
|
+
.reduce((sum, section) => sum +
|
|
221
|
+
(section.pauseBefore ?? 0) +
|
|
222
|
+
((0, utils_1.getCachedDuration)(section.path) ?? 0), 0),
|
|
223
|
+
applicationDuration: sections
|
|
224
|
+
.filter((s) => s.chapter === sets_2.Chapter.APPLICATION)
|
|
225
|
+
.reduce((sum, section) => sum +
|
|
226
|
+
(section.pauseBefore ?? 0) +
|
|
227
|
+
((0, utils_1.getCachedDuration)(section.path) ?? 0), 0),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function getTrainingVideoPaths({ languageInfo, lessonId, }) {
|
|
231
|
+
const trainingVideosDir = (0, utils_1.join)(languageInfo.contentLanguages.trainingVideos, 'course');
|
|
232
|
+
const trainingVideoPath = (0, utils_1.join)(trainingVideosDir, 'videos_compressed', [languageInfo.contentLanguages.trainingVideos, lessonId, 'mp4'].join('.'));
|
|
233
|
+
const trainingVideoNarrationPath = (0, utils_1.join)(trainingVideosDir, 'audio', [languageInfo.contentLanguages.trainingVideos, lessonId, 'mp3'].join('.'));
|
|
234
|
+
const trainingVideoOriginalPath = (0, utils_1.join)(trainingVideosDir, 'videos_export', [languageInfo.contentLanguages.trainingVideos, lessonId, 'm4v'].join('.'));
|
|
235
|
+
const trainingVideoNarrationWithMusicPath = (0, utils_1.join)(trainingVideosDir, 'audio_with_music', [languageInfo.contentLanguages.trainingVideos, lessonId, 'mp3'].join('.'));
|
|
236
|
+
return {
|
|
237
|
+
trainingVideoPath,
|
|
238
|
+
trainingVideoNarrationPath,
|
|
239
|
+
trainingVideoOriginalPath,
|
|
240
|
+
trainingVideoNarrationWithMusicPath,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions, }) {
|
|
244
|
+
if (!lessonId)
|
|
245
|
+
return;
|
|
246
|
+
else if (lessonId.split('.').length !== 3)
|
|
247
|
+
return;
|
|
248
|
+
const [category, setNumber, lessonNumber] = lessonId.split('.');
|
|
249
|
+
const languageId = languageInfo.languageId;
|
|
250
|
+
setInfo =
|
|
251
|
+
setInfo ??
|
|
252
|
+
getSetInfo({
|
|
253
|
+
setId: [category, setNumber].join('.'),
|
|
254
|
+
languageId,
|
|
255
|
+
setIds: languageInfo.setIds,
|
|
256
|
+
t,
|
|
257
|
+
});
|
|
258
|
+
if (!setInfo) {
|
|
259
|
+
console.log('Missing set info for lesson:', lessonId);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const lesson = setInfo.lessons.find((lesson) => lesson.lessonId === lessonId);
|
|
263
|
+
if (!lesson)
|
|
264
|
+
return;
|
|
265
|
+
const thisSetTranslations = getSetTranslations({
|
|
266
|
+
setInfo,
|
|
267
|
+
lessonId,
|
|
268
|
+
languageId,
|
|
269
|
+
t,
|
|
270
|
+
});
|
|
271
|
+
const { sections, applicationDuration, fellowshipDuration } = assembleLessonSections({
|
|
272
|
+
setInfo,
|
|
273
|
+
lesson,
|
|
274
|
+
languageInfo,
|
|
275
|
+
t,
|
|
276
|
+
useSpokenQuestions,
|
|
277
|
+
});
|
|
278
|
+
const passagesString = (0, scripturePassages_1.getPassagesString)(sections
|
|
279
|
+
.filter((section) => section.chapter === sets_2.Chapter.STORY)
|
|
280
|
+
// Slice out any concat passages.
|
|
281
|
+
.slice(0, lesson.s.length)
|
|
282
|
+
.map((section) => {
|
|
283
|
+
const [bibleId, passageId] = section.id.split(';');
|
|
284
|
+
return { passageId, bibleId };
|
|
285
|
+
}));
|
|
231
286
|
const baseInfo = {
|
|
232
287
|
lessonLink: `https://web.waha.app/lesson?lesson-id=${lessonId}&meet-language=${languageId}`,
|
|
233
288
|
lessonNumber,
|
|
234
|
-
lessonTitle,
|
|
289
|
+
lessonTitle: thisSetTranslations?.lessonTitle,
|
|
235
290
|
sections,
|
|
291
|
+
fellowshipDuration,
|
|
292
|
+
applicationDuration,
|
|
236
293
|
...setInfo,
|
|
237
294
|
...lesson,
|
|
238
295
|
...languageInfo,
|
|
239
296
|
};
|
|
240
|
-
const fellowshipDuration = sections
|
|
241
|
-
.filter((s) => s.chapter === sets_2.Chapter.FELLOWSHIP)
|
|
242
|
-
.reduce((sum, section) => sum +
|
|
243
|
-
(section.pauseBefore ?? 0) +
|
|
244
|
-
(mediaDurations_1.mediaDurations[languageInfo.contentLanguages.questionAudio]?.[section.fileName] ?? 0), 0);
|
|
245
|
-
let applicationDuration = sections
|
|
246
|
-
.filter((s) => s.chapter === sets_2.Chapter.APPLICATION)
|
|
247
|
-
.reduce((sum, section) => sum +
|
|
248
|
-
(section.pauseBefore ?? 0) +
|
|
249
|
-
(mediaDurations_1.mediaDurations[languageInfo.contentLanguages.questionAudio]?.[section.fileName] ?? 0), 0);
|
|
250
297
|
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
298
|
return {
|
|
255
299
|
type: 'eq',
|
|
256
300
|
...baseInfo,
|
|
257
|
-
|
|
258
|
-
applicationDuration,
|
|
259
|
-
eq: {
|
|
260
|
-
id: eqId,
|
|
261
|
-
localFileName: eqFileName,
|
|
262
|
-
remoteFileName: eqFileName,
|
|
263
|
-
path: eqPath,
|
|
264
|
-
url: (0, utils_1.firebasePath)(eqPath),
|
|
265
|
-
},
|
|
301
|
+
eqPath: (0, utils_1.join)(languageInfo.contentLanguages.intros, 'introductions', `${languageInfo.contentLanguages.intros}.eq.mp3`),
|
|
266
302
|
};
|
|
267
303
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const videoLessonRemoteFileName = `${videoLessonId}.1080.mp4`;
|
|
293
|
-
const videoLessonLocalFileName = `${videoLessonId}.mp4`;
|
|
294
|
-
const videoPath = (0, utils_1.join)(languageId, 'full_lessons', setInfo.setId, videoLessonRemoteFileName);
|
|
295
|
-
video = {
|
|
296
|
-
id: videoLessonId,
|
|
297
|
-
localFileName: videoLessonLocalFileName,
|
|
298
|
-
remoteFileName: videoLessonRemoteFileName,
|
|
299
|
-
path: videoPath,
|
|
300
|
-
url: (0, utils_1.firebasePath)(videoPath),
|
|
301
|
-
};
|
|
302
|
-
const trainingVideoId = [
|
|
303
|
-
languageInfo.contentLanguages.trainingVideos,
|
|
304
|
-
lessonId,
|
|
305
|
-
].join('.');
|
|
306
|
-
const trainingVideoFileName = `${trainingVideoId}.mp4`;
|
|
307
|
-
// Add the length of the training video to the application duration.
|
|
308
|
-
applicationDuration +=
|
|
309
|
-
mediaDurations_1.mediaDurations[languageInfo.contentLanguages.trainingVideos]?.[trainingVideoFileName] ?? 0;
|
|
310
|
-
const trainingVideoPath = (0, utils_1.join)(languageInfo.contentLanguages.trainingVideos, 'course', 'videos_compressed', trainingVideoFileName);
|
|
311
|
-
trainingVideo = {
|
|
312
|
-
id: trainingVideoId,
|
|
313
|
-
localFileName: trainingVideoFileName,
|
|
314
|
-
remoteFileName: trainingVideoFileName,
|
|
315
|
-
path: trainingVideoPath,
|
|
316
|
-
url: (0, utils_1.firebasePath)(trainingVideoPath),
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
let youtubeLink;
|
|
320
|
-
if (video.remoteFileName && video.remoteFileName in youtubeVideos_1.youtubeVideos)
|
|
321
|
-
youtubeLink =
|
|
322
|
-
'https://www.youtube.com/watch?v=' +
|
|
323
|
-
youtubeVideos_1.youtubeVideos[video.remoteFileName];
|
|
304
|
+
if (setInfo.setId === specialIds_1.specialIds.dmCourseSetId || languageId === 'ase') {
|
|
305
|
+
const tvPaths = getTrainingVideoPaths({ languageInfo, lessonId });
|
|
306
|
+
const videoPath = languageId === 'ase'
|
|
307
|
+
? (0, utils_1.join)(languageId, `full_lessons${languageInfo.assetVersions?.dbs ?? ''}`, setInfo.setId, [languageId, lessonId, '1080', 'mp4'].join('.'))
|
|
308
|
+
: specialIds_1.specialIds.dmCourseVideoOnlyLessonIds.includes(lessonId)
|
|
309
|
+
? tvPaths.trainingVideoPath
|
|
310
|
+
: (0, utils_1.join)(languageId, `full_lessons${languageInfo.assetVersions?.dmc ?? ''}`, setInfo.setId, [
|
|
311
|
+
languageId,
|
|
312
|
+
languageInfo.contentLanguages.trainingVideos,
|
|
313
|
+
lessonId,
|
|
314
|
+
'1080',
|
|
315
|
+
'mp4',
|
|
316
|
+
].join('.'));
|
|
317
|
+
const youtubeLink = (0, utils_1.basename)(videoPath) in youtubeVideos_1.youtubeVideos
|
|
318
|
+
? 'https://www.youtube.com/watch?v=' +
|
|
319
|
+
youtubeVideos_1.youtubeVideos[(0, utils_1.basename)(videoPath)]
|
|
320
|
+
: undefined;
|
|
321
|
+
const videoFullAudioPath = (0, utils_1.join)(languageId, `full_lessons${languageInfo.assetVersions?.dmc ?? ''}`, setInfo.setId, [
|
|
322
|
+
languageId,
|
|
323
|
+
languageInfo.contentLanguages.trainingVideos,
|
|
324
|
+
lessonId,
|
|
325
|
+
'mp3',
|
|
326
|
+
].join('.'));
|
|
327
|
+
const videoStoryAudioPath = (0, utils_1.join)(languageId, `full_lessons${languageInfo.assetVersions?.dmc ?? ''}`, setInfo.setId, [languageId, lessonId, 'story', 'mp3'].join('.'));
|
|
324
328
|
return {
|
|
325
329
|
type: 'video',
|
|
326
330
|
...baseInfo,
|
|
327
|
-
|
|
328
|
-
applicationDuration,
|
|
331
|
+
...tvPaths,
|
|
329
332
|
lessonSectionHeader: thisSetTranslations?.sectionHeaders?.[lessonId],
|
|
330
333
|
lessonSectionBody: thisSetTranslations?.sectionBodies?.[lessonId],
|
|
331
|
-
|
|
332
|
-
|
|
334
|
+
videoPath,
|
|
335
|
+
fullPath: lesson.s.length > 0 && languageId !== 'ase'
|
|
336
|
+
? videoFullAudioPath
|
|
337
|
+
: undefined,
|
|
338
|
+
storyPath: lesson.s.length > 0 && languageId !== 'ase'
|
|
339
|
+
? videoStoryAudioPath
|
|
340
|
+
: undefined,
|
|
333
341
|
youtubeLink,
|
|
334
342
|
passagesString,
|
|
335
343
|
};
|
|
336
344
|
}
|
|
337
345
|
else {
|
|
338
|
-
const
|
|
339
|
-
const
|
|
340
|
-
const fullLessonFileName = `${fullLessonId}.mp3`;
|
|
341
|
-
const fullLessonPath = (0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, fullLessonFileName);
|
|
342
|
-
const storyId = fullLessonId + '.story';
|
|
343
|
-
const storyFileName = `${storyId}.mp3`;
|
|
344
|
-
const storyPath = (0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, storyFileName);
|
|
346
|
+
const fullPath = (0, utils_1.join)(languageId, `full_lessons${languageInfo.assetVersions?.dbs ?? ''}`, setInfo.setId, `${languageId}.${lessonId}.mp3`);
|
|
347
|
+
const storyPath = fullPath.replace('.mp3', '.story.mp3');
|
|
345
348
|
return {
|
|
346
349
|
type: 'dbs',
|
|
347
350
|
...baseInfo,
|
|
348
|
-
fellowshipDuration,
|
|
349
|
-
applicationDuration,
|
|
350
351
|
passagesString,
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
remoteFileName: fullLessonFileName,
|
|
355
|
-
path: fullLessonPath,
|
|
356
|
-
url: (0, utils_1.firebasePath)(fullLessonPath),
|
|
357
|
-
},
|
|
358
|
-
story: {
|
|
359
|
-
id: storyId,
|
|
360
|
-
localFileName: storyFileName,
|
|
361
|
-
remoteFileName: storyFileName,
|
|
362
|
-
path: storyPath,
|
|
363
|
-
url: (0, utils_1.firebasePath)(storyPath),
|
|
364
|
-
},
|
|
365
|
-
dbsCast: {
|
|
366
|
-
remoteFileName: `${languageId}.${lessonId}.1080.mp4`,
|
|
367
|
-
url: (0, utils_1.firebasePath)((0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, `${languageId}.${lessonId}.1080.mp4`)),
|
|
368
|
-
},
|
|
352
|
+
fullPath,
|
|
353
|
+
storyPath,
|
|
354
|
+
dbsCastPath: (0, utils_1.join)(languageId, `full_lessons${languageInfo.assetVersions?.cast ?? ''}`, setInfo.setId, [languageId, lessonId, '1080', 'mp4'].join('.')),
|
|
369
355
|
};
|
|
370
356
|
}
|
|
371
357
|
}
|
|
372
|
-
function convertSToString(time) {
|
|
373
|
-
let secondsNumber;
|
|
374
|
-
let minutesNumber;
|
|
375
|
-
let hoursNumber;
|
|
376
|
-
let secondsString;
|
|
377
|
-
let minutesString;
|
|
378
|
-
let hoursString;
|
|
379
|
-
if (time > 0) {
|
|
380
|
-
// If the time is greater than an hour, we need to show the hour of the time as well.
|
|
381
|
-
if (time >= 3600) {
|
|
382
|
-
secondsNumber = Math.floor(time % 60);
|
|
383
|
-
minutesNumber = Math.floor((time / 60) % 60);
|
|
384
|
-
hoursNumber = Math.floor((time / (60 * 60)) % 60);
|
|
385
|
-
secondsString = secondsNumber.toString();
|
|
386
|
-
minutesString =
|
|
387
|
-
minutesNumber < 10
|
|
388
|
-
? '0' + minutesNumber.toString()
|
|
389
|
-
: minutesNumber.toString();
|
|
390
|
-
hoursString =
|
|
391
|
-
hoursNumber < 10 ? '0' + hoursNumber.toString() : hoursNumber.toString();
|
|
392
|
-
return `${hoursString}:${minutesString}:${secondsString}`;
|
|
393
|
-
}
|
|
394
|
-
else {
|
|
395
|
-
secondsNumber = Math.floor(time % 60);
|
|
396
|
-
minutesNumber = Math.floor((time / 60) % 60);
|
|
397
|
-
minutesString =
|
|
398
|
-
minutesNumber < 10
|
|
399
|
-
? '0' + minutesNumber.toString()
|
|
400
|
-
: minutesNumber.toString();
|
|
401
|
-
secondsString =
|
|
402
|
-
secondsNumber < 10
|
|
403
|
-
? '0' + secondsNumber.toString()
|
|
404
|
-
: secondsNumber.toString();
|
|
405
|
-
return `${minutesString}:${secondsString}`;
|
|
406
|
-
}
|
|
407
|
-
} // If the time is greater than the max allowed, just show the max.
|
|
408
|
-
else {
|
|
409
|
-
return '00:00';
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
358
|
/** Determines whether a lesson should be visible in Waha. */
|
|
413
|
-
function shouldShowLesson(lessonInfo
|
|
359
|
+
function shouldShowLesson(lessonInfo) {
|
|
414
360
|
if (lessonInfo?.lessonTitle === undefined || lessonInfo.lessonTitle === '') {
|
|
415
361
|
console.log('Missing lesson info or lessonTitle lesson:', lessonInfo?.lessonId);
|
|
416
362
|
return false;
|
|
417
363
|
}
|
|
418
364
|
else if (lessonInfo.type === 'eq' &&
|
|
419
|
-
|
|
365
|
+
(0, utils_1.getCachedDuration)(lessonInfo.eqPath) === undefined) {
|
|
420
366
|
console.log('Missing remote eq file for lesson:', lessonInfo.lessonId);
|
|
421
367
|
return false;
|
|
422
368
|
}
|
|
423
369
|
else if (lessonInfo.type === 'dbs' &&
|
|
424
|
-
|
|
425
|
-
undefined) {
|
|
370
|
+
(0, utils_1.getCachedDuration)(lessonInfo.fullPath) === undefined) {
|
|
426
371
|
console.log('Missing remote dbs file for lesson:', lessonInfo.lessonId);
|
|
427
372
|
return false;
|
|
428
373
|
}
|
|
429
374
|
else if (lessonInfo.type === 'video' &&
|
|
430
|
-
lessonInfo.
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
console.log('Missing remote video for lesson:', lessonInfo.video.remoteFileName);
|
|
375
|
+
lessonInfo.videoPath &&
|
|
376
|
+
(0, utils_1.getCachedDuration)(lessonInfo.videoPath) === undefined) {
|
|
377
|
+
console.log('Missing remote video for lesson:', lessonInfo.lessonId);
|
|
434
378
|
return false;
|
|
435
379
|
}
|
|
436
380
|
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.getDurationFromFile)(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;
|