waha-shared 1.0.332 → 1.0.335
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 +10 -0
- package/dist/data/bibleStatuses/bibleStatuses.json +32 -2013
- package/dist/data/bibleTexts/bibleTexts.json +10 -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 +139 -48
- 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 +24823 -675
- package/dist/data/releaseNotes/releaseNotes.json +2 -9
- 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 +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
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,177 @@ 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 ? videoFullAudioPath : undefined,
|
|
336
|
+
storyPath: lesson.s.length > 0 ? videoStoryAudioPath : undefined,
|
|
333
337
|
youtubeLink,
|
|
334
338
|
passagesString,
|
|
335
339
|
};
|
|
336
340
|
}
|
|
337
341
|
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);
|
|
342
|
+
const fullPath = (0, utils_1.join)(languageId, `full_lessons${languageInfo.assetVersions?.dbs ?? ''}`, setInfo.setId, `${languageId}.${lessonId}.mp3`);
|
|
343
|
+
const storyPath = fullPath.replace('.mp3', '.story.mp3');
|
|
345
344
|
return {
|
|
346
345
|
type: 'dbs',
|
|
347
346
|
...baseInfo,
|
|
348
|
-
fellowshipDuration,
|
|
349
|
-
applicationDuration,
|
|
350
347
|
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
|
-
},
|
|
348
|
+
fullPath,
|
|
349
|
+
storyPath,
|
|
350
|
+
dbsCastPath: (0, utils_1.join)(languageId, `full_lessons${languageInfo.assetVersions?.cast ?? ''}`, setInfo.setId, [languageId, lessonId, '1080', 'mp4'].join('.')),
|
|
369
351
|
};
|
|
370
352
|
}
|
|
371
353
|
}
|
|
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
354
|
/** Determines whether a lesson should be visible in Waha. */
|
|
413
|
-
function shouldShowLesson(lessonInfo
|
|
355
|
+
function shouldShowLesson(lessonInfo) {
|
|
414
356
|
if (lessonInfo?.lessonTitle === undefined || lessonInfo.lessonTitle === '') {
|
|
415
357
|
console.log('Missing lesson info or lessonTitle lesson:', lessonInfo?.lessonId);
|
|
416
358
|
return false;
|
|
417
359
|
}
|
|
418
360
|
else if (lessonInfo.type === 'eq' &&
|
|
419
|
-
|
|
361
|
+
(0, utils_1.getCachedDuration)(lessonInfo.eqPath) === undefined) {
|
|
420
362
|
console.log('Missing remote eq file for lesson:', lessonInfo.lessonId);
|
|
421
363
|
return false;
|
|
422
364
|
}
|
|
423
365
|
else if (lessonInfo.type === 'dbs' &&
|
|
424
|
-
|
|
425
|
-
undefined) {
|
|
366
|
+
(0, utils_1.getCachedDuration)(lessonInfo.fullPath) === undefined) {
|
|
426
367
|
console.log('Missing remote dbs file for lesson:', lessonInfo.lessonId);
|
|
427
368
|
return false;
|
|
428
369
|
}
|
|
429
370
|
else if (lessonInfo.type === 'video' &&
|
|
430
|
-
lessonInfo.
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
console.log('Missing remote video for lesson:', lessonInfo.video.remoteFileName);
|
|
371
|
+
lessonInfo.videoPath &&
|
|
372
|
+
(0, utils_1.getCachedDuration)(lessonInfo.videoPath) === undefined) {
|
|
373
|
+
console.log('Missing remote video for lesson:', lessonInfo.lessonId);
|
|
434
374
|
return false;
|
|
435
375
|
}
|
|
436
376
|
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;
|