waha-shared 1.0.319 → 1.0.326

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.
@@ -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 getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions, }) {
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
- lessonTitle =
84
- thisSetTranslations?.lessonTitles?.[`${englishEquivalent?.setId}.${lessonNumber}`];
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
- lessonTitle = thisSetTranslations?.lessonTitles?.[lessonId];
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
- fileName: '',
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
- fileName,
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
- fileName: `${languageInfo.contentLanguages.intros}.eq.mp3`,
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
- fileName: introFileName,
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
- ftbFileName: ftbRequired
166
- ? `${languageInfo.contentLanguages.ftbs}.${passageInfo.book}.mp3`
147
+ ftbPath: ftbRequired
148
+ ? (0, utils_1.join)(languageInfo.contentLanguages.ftbs, 'ftb', [languageInfo.contentLanguages.ftbs, passageInfo.book, 'mp3'].join('.'))
167
149
  : undefined,
168
- fileName: bibleStatuses_1.bibleStatuses[bibleId]?.customPassages?.[passageId]
169
- ? passageId + '.mp3'
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
- fileName: passageInfo.chapterAudioFileName,
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
- ftbFileName: ftbRequired
203
- ? `${languageInfo.contentLanguages.ftbs}.${passageInfo.book}.mp3`
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
- fileName,
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
- fellowshipDuration,
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
- else if (setInfo.setId === specialIds_1.specialIds.dmCourseSetId ||
269
- languageId === 'ase') {
270
- let video;
271
- let trainingVideo;
272
- if (languageId === 'ase') {
273
- const aseVideoLessonId = `${languageId}.${lessonId}`;
274
- const aseVideoLocalFileName = `${aseVideoLessonId}.mp4`;
275
- const aseVideoRemoteFileName = `${aseVideoLessonId}.1080.mp4`;
276
- const aseVideoPath = (0, utils_1.join)(languageId, 'full_lessons', setInfo.setId, aseVideoRemoteFileName);
277
- video = {
278
- id: aseVideoLessonId,
279
- localFileName: aseVideoLocalFileName,
280
- remoteFileName: aseVideoRemoteFileName,
281
- path: aseVideoPath,
282
- url: (0, utils_1.firebasePath)(aseVideoPath),
283
- };
284
- trainingVideo = undefined;
285
- }
286
- else {
287
- const videoLessonId = [
288
- languageId,
289
- languageInfo.contentLanguages.trainingVideos,
290
- lessonId,
291
- ].join('.');
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
- fellowshipDuration,
328
- applicationDuration,
331
+ ...tvPaths,
329
332
  lessonSectionHeader: thisSetTranslations?.sectionHeaders?.[lessonId],
330
333
  lessonSectionBody: thisSetTranslations?.sectionBodies?.[lessonId],
331
- video,
332
- trainingVideo,
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 assetVersion = languageInfo.audioAssetVersion ?? '';
339
- const fullLessonId = `${languageId}.${lessonId}`;
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
- full: {
352
- id: fullLessonId,
353
- localFileName: fullLessonFileName,
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, languageInfo) {
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
- mediaDurations_1.mediaDurations[languageInfo.contentLanguages.intros]?.[lessonInfo.eq.remoteFileName] === undefined) {
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
- mediaDurations_1.mediaDurations[lessonInfo.languageId]?.[lessonInfo.full.remoteFileName] ===
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.video &&
431
- mediaDurations_1.mediaDurations[lessonInfo.languageId]?.[lessonInfo.video.remoteFileName] ===
432
- undefined) {
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 firebasePath: (path: string) => string;
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;