waha-shared 1.0.307 → 1.0.308

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.
Files changed (102) hide show
  1. package/dist/data/areas/areas.json +48 -50
  2. package/dist/data/areas/areas.schema.json +2 -2
  3. package/dist/data/areas/areas.zod.d.ts +1 -1
  4. package/dist/data/areas/areas.zod.js +1 -1
  5. package/dist/data/areas/index.d.ts +1 -1
  6. package/dist/data/bibleAudios/bibleAudios.json +314 -581
  7. package/dist/data/bibleAudios/bibleAudios.schema.json +8 -17
  8. package/dist/data/bibleAudios/bibleAudios.zod.d.ts +2 -43
  9. package/dist/data/bibleAudios/bibleAudios.zod.js +7 -16
  10. package/dist/data/bibleAudios/index.d.ts +1 -2
  11. package/dist/data/bibleBooks/bibleBooks.json +782 -784
  12. package/dist/data/bibleStatuses/bibleStatuses.json +8284 -7785
  13. package/dist/data/bibleStatuses/bibleStatuses.schema.json +0 -6
  14. package/dist/data/bibleStatuses/bibleStatuses.zod.d.ts +0 -1
  15. package/dist/data/bibleStatuses/bibleStatuses.zod.js +0 -4
  16. package/dist/data/bibleStatuses/index.d.ts +0 -1
  17. package/dist/data/bibleTexts/bibleTexts.json +184 -269
  18. package/dist/data/bibleTexts/bibleTexts.schema.json +0 -7
  19. package/dist/data/bibleTexts/bibleTexts.zod.d.ts +0 -28
  20. package/dist/data/bibleTexts/bibleTexts.zod.js +3 -10
  21. package/dist/data/bibleTexts/index.d.ts +0 -2
  22. package/dist/data/clones/clones.json +0 -2
  23. package/dist/data/countries/countries.json +480 -480
  24. package/dist/data/countriesAndLanguages/countriesAndLanguages.json +6798 -6800
  25. package/dist/data/crowdinLanguages/crowdinLanguages.json +1438 -1440
  26. package/dist/data/crowdinLanguages/crowdinLanguages.schema.json +3 -3
  27. package/dist/data/crowdinLanguages/crowdinLanguages.zod.d.ts +2 -2
  28. package/dist/data/crowdinLanguages/crowdinLanguages.zod.js +2 -2
  29. package/dist/data/crowdinLanguages/index.d.ts +2 -2
  30. package/dist/data/curriculumFoundations/curriculumFoundations.json +69 -86
  31. package/dist/data/curriculumQuestions/curriculumQuestions.json +192 -194
  32. package/dist/data/curriculumTopics/curriculumTopics.json +8 -51
  33. package/dist/data/dblAudioLicenses/dblAudioLicenses.json +7 -36
  34. package/dist/data/dblTextLicenses/dblTextLicenses.json +4 -4
  35. package/dist/data/iosVoiceOverLanguages/iosVoiceOverLanguages.json +1 -1
  36. package/dist/data/iso6933LanguageCodes/iso6933LanguageCodes.json +1 -1
  37. package/dist/data/languageAssets/index.d.ts +1 -0
  38. package/dist/data/languageAssets/index.js +7 -0
  39. package/dist/data/languageAssets/languageAssets.json +45406 -0
  40. package/dist/data/languageAssets/languageAssets.schema.json +19 -0
  41. package/dist/data/languageAssets/languageAssets.zod.d.ts +3 -0
  42. package/dist/data/languageAssets/languageAssets.zod.js +7 -0
  43. package/dist/data/languages/index.d.ts +10 -20
  44. package/dist/data/languages/languages.json +2008 -2983
  45. package/dist/data/languages/languages.schema.json +47 -93
  46. package/dist/data/languages/languages.zod.d.ts +30 -93
  47. package/dist/data/languages/languages.zod.js +37 -59
  48. package/dist/data/lessonPauses/index.d.ts +1 -0
  49. package/dist/data/lessonPauses/lessonPauses.json +3 -2
  50. package/dist/data/lessonPauses/lessonPauses.schema.json +11 -1
  51. package/dist/data/lessonPauses/lessonPauses.zod.d.ts +1 -0
  52. package/dist/data/lessonPauses/lessonPauses.zod.js +3 -0
  53. package/dist/data/mediaDurations/mediaDurations.json +4231 -33446
  54. package/dist/data/mediaDurations/mediaDurations.schema.json +1 -1
  55. package/dist/data/mediaDurations/mediaDurations.zod.js +1 -1
  56. package/dist/data/notification/index.d.ts +1 -1
  57. package/dist/data/notification/notification.json +169 -93
  58. package/dist/data/phoneLanguages/phoneLanguages.json +635 -637
  59. package/dist/data/questions/questions.json +147 -149
  60. package/dist/data/releaseNotes/releaseNotes.json +124 -126
  61. package/dist/data/screenshots/screenshots.json +1 -1
  62. package/dist/data/scripts/scripts.json +1 -6
  63. package/dist/data/sets/index.d.ts +3 -3
  64. package/dist/data/sets/sets.json +476 -1766
  65. package/dist/data/sets/sets.schema.json +1 -1
  66. package/dist/data/sets/sets.zod.d.ts +3 -3
  67. package/dist/data/sets/sets.zod.js +6 -3
  68. package/dist/data/translationsApp/index.d.ts +0 -1
  69. package/dist/data/translationsApp/translationsApp.json +1582 -1623
  70. package/dist/data/translationsApp/translationsApp.schema.json +0 -1
  71. package/dist/data/translationsApp/translationsApp.zod.d.ts +0 -2
  72. package/dist/data/translationsApp/translationsApp.zod.js +0 -1
  73. package/dist/data/translationsFtb/translationsFtb.json +613 -69
  74. package/dist/data/translationsIntroduction/translationsIntroduction.json +1 -1
  75. package/dist/data/translationsQuestion/translationsQuestion.json +33 -65
  76. package/dist/data/translationsSet/translationsSet.json +19565 -30302
  77. package/dist/data/translationsSet/translationsSet.schema.json +4 -3
  78. package/dist/data/translationsSet/translationsSet.zod.js +4 -4
  79. package/dist/data/translationsSolarSpeaker/translationsSolarSpeaker.json +1 -1
  80. package/dist/data/translationsSpokenQuestion/translationsSpokenQuestion.json +33 -65
  81. package/dist/data/youtubePlaylists/youtubePlaylists.json +10 -10
  82. package/dist/data/youtubeVideos/youtubeVideos.json +82 -82
  83. package/dist/functions/languages.d.ts +7 -6
  84. package/dist/functions/languages.js +53 -51
  85. package/dist/functions/scripturePassages.d.ts +49 -34
  86. package/dist/functions/scripturePassages.js +304 -167
  87. package/dist/functions/sets.d.ts +52 -13
  88. package/dist/functions/sets.js +205 -193
  89. package/dist/types/bibleChapters.d.ts +2 -2
  90. package/dist/types/bibleChapters.js +1 -1
  91. package/dist/types/languages.d.ts +9 -39
  92. package/dist/types/languages.js +0 -2
  93. package/dist/types/sets.d.ts +11 -21
  94. package/package.json +1 -1
  95. package/dist/data/firebase.d.ts +0 -1
  96. package/dist/data/firebase.js +0 -4
  97. package/dist/functions/bibleBooks.d.ts +0 -16
  98. package/dist/functions/bibleBooks.js +0 -101
  99. package/dist/functions/bibles.d.ts +0 -44
  100. package/dist/functions/bibles.js +0 -291
  101. package/dist/functions/utils.d.ts +0 -2
  102. package/dist/functions/utils.js +0 -8
@@ -1,20 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.firebaseUrl = void 0;
3
4
  exports.getSetInfo = getSetInfo;
4
5
  exports.getLessonInfo = getLessonInfo;
5
6
  exports.convertSToString = convertSToString;
6
7
  exports.shouldShowLesson = shouldShowLesson;
7
- exports.getExpectedLessonDuration = getExpectedLessonDuration;
8
- const bibleStatuses_1 = require("../data/bibleStatuses");
8
+ exports.calculateSectionTimings = calculateSectionTimings;
9
+ exports.getSupportsVerseHighlight = getSupportsVerseHighlight;
10
+ const languageAssets_1 = require("../data/languageAssets");
9
11
  const mediaDurations_1 = require("../data/mediaDurations");
12
+ const questions_1 = require("../data/questions");
10
13
  const sets_1 = require("../data/sets");
11
14
  const specialIds_1 = require("../data/specialIds");
12
15
  const youtubeVideos_1 = require("../data/youtubeVideos");
13
16
  const scripturePassages_1 = require("../functions/scripturePassages");
14
17
  const sets_2 = require("../types/sets");
15
- const bibles_1 = require("./bibles");
16
- const utils_1 = require("./utils");
17
- function getSetInfo({ setId, languageId, setIds, t, }) {
18
+ exports.firebaseUrl = 'https://firebasestorage.googleapis.com/v0/b/waha-app-db.appspot.com/o/';
19
+ function getSetInfo({ setId, meetLanguageId, setIds, t, }) {
18
20
  const idComponents = setId.split('.');
19
21
  const category = idComponents[0] === '01'
20
22
  ? 'Foundations'
@@ -28,7 +30,7 @@ function getSetInfo({ setId, languageId, setIds, t, }) {
28
30
  const thisSet = sets_1.sets.find((set) => set.setId === setId);
29
31
  if (!thisSet)
30
32
  return;
31
- if (languageId === 'ase') {
33
+ if (meetLanguageId === 'ase') {
32
34
  const englishEquivalent = sets_1.sets.find((set) => set.name === thisSet.name && !set.comment);
33
35
  thisSetTranslations = t.sets.find((setTranslations) => setTranslations.setId === englishEquivalent?.setId);
34
36
  }
@@ -46,26 +48,26 @@ function getSetInfo({ setId, languageId, setIds, t, }) {
46
48
  setSubtitle: category === 'Topics'
47
49
  ? (thisSetTranslations?.setTag ?? '')
48
50
  : `${categoryName} ${parseInt(setNumber)}`,
49
- setLink: `https://web.waha.app/set?set-id=${setId}&meet-language=${languageId}`,
50
- languageId,
51
+ setLink: `https://web.waha.app/set?set-id=${setId}&meet-language=${meetLanguageId}`,
52
+ meetLanguageId,
51
53
  setNumber,
52
54
  category,
53
55
  };
54
56
  return toReturn;
55
57
  }
56
- function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions, }) {
58
+ function getLessonInfo({ lessonId, meetLanguageInfo, setInfo, t, useSpokenQuestions, }) {
57
59
  if (!lessonId)
58
60
  return;
59
61
  else if (lessonId.split('.').length !== 3)
60
62
  return;
61
63
  const [category, setNumber, lessonNumber] = lessonId.split('.');
62
- const languageId = languageInfo.languageId;
64
+ const meetLanguageId = meetLanguageInfo.languageId;
63
65
  setInfo =
64
66
  setInfo ??
65
67
  getSetInfo({
66
68
  setId: [category, setNumber].join('.'),
67
- languageId,
68
- setIds: languageInfo.setIds,
69
+ meetLanguageId,
70
+ setIds: meetLanguageInfo.setIds,
69
71
  t,
70
72
  });
71
73
  if (!setInfo) {
@@ -77,7 +79,7 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
77
79
  return;
78
80
  let thisSetTranslations;
79
81
  let lessonTitle;
80
- if (languageId === 'ase') {
82
+ if (meetLanguageId === 'ase') {
81
83
  const englishEquivalent = sets_1.sets.find((set) => set.name === setInfo.name && !set.comment);
82
84
  thisSetTranslations = t.sets.find((setTranslations) => setTranslations.setId === englishEquivalent?.setId);
83
85
  lessonTitle =
@@ -87,231 +89,171 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
87
89
  thisSetTranslations = t.sets.find((setTranslations) => setTranslations.setId === setInfo?.setId);
88
90
  lessonTitle = thisSetTranslations?.lessonTitles?.[lessonId];
89
91
  }
90
- const fellowshipQuestions = languageInfo.questionSets.find((questionSet) => questionSet.questionSetId === lesson.f)?.questions ?? [];
92
+ const fellowshipQuestions = meetLanguageInfo.questionSets.find((questionSet) => questionSet.questionSetId === lesson.f)?.questions ?? [];
91
93
  const fellowshipQuestionsText = fellowshipQuestions.map((questionId) => useSpokenQuestions ? t.questionsSpoken[questionId] : t.questions[questionId]);
92
- const applicationQuestions = languageInfo.questionSets.find((questionSet) => questionSet.questionSetId === lesson.a)?.questions ?? [];
94
+ const applicationQuestions = meetLanguageInfo.questionSets.find((questionSet) => questionSet.questionSetId === lesson.a)?.questions ?? [];
93
95
  const applicationQuestionsText = applicationQuestions.map((questionId) => useSpokenQuestions ? t.questionsSpoken[questionId] : t.questions[questionId]);
94
96
  const sections = [
95
97
  {
96
98
  id: 'title',
99
+ isChapter: false,
100
+ header: lessonTitle ?? '',
97
101
  index: 0,
102
+ chapter: null,
98
103
  indexWithinChapter: 0,
99
- fileName: '',
100
- languageId,
104
+ text: lessonTitle ?? '',
105
+ hasBeep: false,
101
106
  },
102
107
  ];
103
108
  fellowshipQuestions.forEach((questionId, index) => {
104
- const fileName = `${languageInfo.contentLanguages.questionAudio}.${questionId}.mp3`;
109
+ const hasBeep = questions_1.questions.find((question) => question.questionId === questionId)
110
+ ?.hasBeep ?? false;
105
111
  sections.push({
106
112
  id: questionId,
107
- languageId: languageInfo.contentLanguages.questionAudio,
113
+ isChapter: index === 0,
108
114
  index: sections.length,
109
- header: index === 0 ? t.fellowship : undefined,
115
+ header: index === 0 ? t.fellowship : null,
110
116
  chapter: sets_2.Chapter.FELLOWSHIP,
111
117
  indexWithinChapter: index,
112
118
  text: fellowshipQuestionsText[index],
113
- fileName,
119
+ hasBeep,
114
120
  });
115
121
  });
122
+ let introLength = undefined;
116
123
  if (specialIds_1.specialIds.evaluationQuestionLessonIds.includes(lesson.lessonId)) {
117
124
  sections.push({
118
125
  id: 'eq',
119
- languageId: languageInfo.contentLanguages.intros,
126
+ isChapter: false,
120
127
  index: sections.length,
121
128
  chapter: sets_2.Chapter.INTRODUCTION,
122
129
  indexWithinChapter: 0,
123
- pauseBefore: 0,
130
+ header: null,
124
131
  text: t.introductions.introductions.eq,
125
- fileName: `${languageInfo.contentLanguages.intros}.eq.mp3`,
132
+ hasBeep: false,
126
133
  });
127
134
  }
128
135
  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
136
  sections.push({
136
137
  id: 'introduction',
137
- languageId: languageInfo.contentLanguages.intros,
138
+ isChapter: false,
138
139
  index: sections.length,
139
140
  chapter: sets_2.Chapter.INTRODUCTION,
140
141
  indexWithinChapter: 0,
141
- pauseBefore: languageInfo.lessonPauses.beforeFtb,
142
142
  header: t.introductions.introduction,
143
143
  text: t.introductions.introductions[lessonId],
144
- fileName: introFileName,
144
+ hasBeep: false,
145
145
  });
146
+ const introLanguageId = meetLanguageInfo.introBridge ?? meetLanguageId;
147
+ introLength =
148
+ mediaDurations_1.mediaDurations[introLanguageId]?.[`${introLanguageId}.${lessonId}.mp3`];
146
149
  }
147
- let lastBook = null;
148
- lesson.s.forEach((passageId, index) => {
149
- const bibleId = (0, bibles_1.getBibleForPassage)({
150
- languageInfo,
151
- lessonCategory: setInfo.category,
152
- passageId,
153
- });
154
- const passageInfo = (0, scripturePassages_1.parseVerseRange)(passageId);
155
- const ftbRequired = lastBook !== passageInfo.book;
150
+ lesson.s?.forEach((passageId, index) => {
156
151
  sections.push({
157
- id: `${bibleId};${passageId}`,
158
- languageId,
152
+ id: passageId,
153
+ isChapter: index === 0,
154
+ hasBeep: false,
159
155
  index: sections.length,
160
156
  chapter: sets_2.Chapter.STORY,
161
157
  indexWithinChapter: index,
162
- pauseBefore: ftbRequired
163
- ? languageInfo.lessonPauses.beforeFtb
164
- : languageInfo.lessonPauses.betweenPassages,
165
- ftbFileName: ftbRequired
166
- ? `${languageInfo.contentLanguages.ftbs}.${passageInfo.book}.mp3`
167
- : undefined,
168
- fileName: bibleStatuses_1.bibleStatuses[bibleId]?.customPassages?.[passageId]
169
- ? passageId + '.mp3'
170
- : passageInfo.chapterAudioFileName,
171
- });
172
- lastBook = passageInfo.book;
173
- });
174
- const passagesString = (0, scripturePassages_1.getPassagesString)(sections
175
- .filter((section) => section.chapter === sets_2.Chapter.STORY)
176
- .map((section) => {
177
- const [bibleId, passageId] = section.id.split(';');
178
- return { passageId, bibleId };
179
- }));
180
- let concatStoryIndex = lesson.s.length;
181
- languageInfo.bibleModifiers?.forEach((modifier) => {
182
- lesson.s.forEach((passageId) => {
183
- if (!(0, bibles_1.shouldConcatBibleWith)({
184
- modifier,
185
- lessonCategory: setInfo.category,
186
- passageId,
187
- }))
188
- return;
189
- const passageInfo = (0, scripturePassages_1.parseVerseRange)(passageId);
190
- const ftbRequired = lastBook !== passageInfo.book;
191
- sections.push({
192
- id: `${modifier.bibleId};${passageId}`,
193
- languageId,
194
- index: sections.length,
195
- chapter: sets_2.Chapter.STORY,
196
- indexWithinChapter: concatStoryIndex,
197
- fileName: passageInfo.chapterAudioFileName,
198
- pauseBefore: ftbRequired
199
- ? languageInfo.lessonPauses.beforeFtb
200
- : languageInfo.lessonPauses.betweenPassages,
201
- ftbFileName: ftbRequired
202
- ? `${languageInfo.contentLanguages.ftbs}.${passageInfo.book}.mp3`
203
- : undefined,
204
- });
205
- lastBook = passageInfo.book;
206
- concatStoryIndex++;
158
+ text: '',
159
+ header: (0, scripturePassages_1.getPassagesString)([passageId], meetLanguageInfo),
207
160
  });
208
161
  });
209
162
  applicationQuestions.forEach((questionId, index) => {
210
- const fileName = questionId.includes('video')
211
- ? `${languageInfo.contentLanguages.trainingVideos}.${lessonId}.mp3`
212
- : `${languageInfo.contentLanguages.questionAudio}.${questionId}.mp3`;
163
+ const hasBeep = questions_1.questions.find((question) => question.questionId === questionId)
164
+ ?.hasBeep ?? false;
213
165
  sections.push({
214
166
  id: questionId,
215
- languageId: questionId.includes('video')
216
- ? languageInfo.contentLanguages.trainingVideos
217
- : languageInfo.contentLanguages.questionAudio,
167
+ isChapter: index === 0,
168
+ hasBeep,
218
169
  index: sections.length,
219
170
  chapter: sets_2.Chapter.APPLICATION,
220
171
  indexWithinChapter: index,
221
- pauseBefore: index === 0 ? languageInfo.lessonPauses.afterStory : undefined,
222
- header: index === 0 ? t.application : undefined,
172
+ header: index === 0 ? t.application : null,
223
173
  text: questionId.includes('video')
224
174
  ? t.training_video
225
175
  : applicationQuestionsText[index],
226
- fileName,
227
176
  });
228
177
  });
229
178
  const baseInfo = {
230
- lessonLink: `https://web.waha.app/lesson?lesson-id=${lessonId}&meet-language=${languageId}`,
179
+ lessonLink: `https://web.waha.app/lesson?lesson-id=${lessonId}&meet-language=${meetLanguageId}`,
231
180
  lessonNumber,
232
181
  lessonTitle,
233
182
  sections,
234
183
  ...setInfo,
235
184
  ...lesson,
236
- ...languageInfo,
237
185
  };
238
- const fellowshipDuration = sections
239
- .filter((s) => s.chapter === sets_2.Chapter.FELLOWSHIP)
240
- .reduce((sum, section) => sum +
241
- (section.pauseBefore ?? 0) +
242
- (mediaDurations_1.mediaDurations[languageInfo.contentLanguages.questionAudio]?.[section.fileName] ?? 0), 0);
243
- let applicationDuration = sections
244
- .filter((s) => s.chapter === sets_2.Chapter.APPLICATION)
245
- .reduce((sum, section) => sum +
246
- (section.pauseBefore ?? 0) +
247
- (mediaDurations_1.mediaDurations[languageInfo.contentLanguages.questionAudio]?.[section.fileName] ?? 0), 0);
186
+ const fellowshipDuration = fellowshipQuestions.reduce((sum, questionId) => {
187
+ const key = `${meetLanguageId}.${questionId}.mp3`;
188
+ return sum + (mediaDurations_1.mediaDurations[meetLanguageId]?.[key] ?? 0);
189
+ }, 0);
190
+ let applicationDuration = applicationQuestions.reduce((sum, questionId) => {
191
+ return (sum +
192
+ (mediaDurations_1.mediaDurations[meetLanguageId]?.[`${meetLanguageId}.${questionId}.mp3`] ??
193
+ 0));
194
+ }, 0);
248
195
  if (specialIds_1.specialIds.evaluationQuestionLessonIds.includes(lesson.lessonId)) {
249
- const eqId = `${languageInfo.contentLanguages.intros}.eq`;
250
- const eqFileName = `${eqId}.mp3`;
251
- const eqPath = (0, utils_1.join)(languageInfo.contentLanguages.intros, 'introductions', eqFileName);
196
+ const languageId = meetLanguageInfo.introBridge ?? meetLanguageId;
197
+ const id = `${languageId}.eq`;
198
+ const fileName = `${id}.mp3`;
199
+ const path = `${languageId}/introductions/${fileName}`;
252
200
  return {
253
201
  type: 'eq',
254
202
  ...baseInfo,
255
203
  fellowshipDuration,
256
204
  applicationDuration,
257
205
  eq: {
258
- id: eqId,
259
- localFileName: eqFileName,
260
- remoteFileName: eqFileName,
261
- path: eqPath,
262
- url: (0, utils_1.firebasePath)(eqPath),
206
+ id,
207
+ languageId,
208
+ localFileName: fileName,
209
+ remoteFileName: fileName,
210
+ path,
211
+ url: exports.firebaseUrl + encodeURIComponent(path) + `?alt=media`,
263
212
  },
264
213
  };
265
214
  }
266
215
  else if (setInfo.setId === specialIds_1.specialIds.dmCourseSetId ||
267
- languageId === 'ase') {
216
+ meetLanguageId === 'ase') {
268
217
  let video;
269
218
  let trainingVideo;
270
- if (languageId === 'ase') {
271
- const aseVideoLessonId = `${languageId}.${lessonId}`;
272
- const aseVideoLocalFileName = `${aseVideoLessonId}.mp4`;
273
- const aseVideoRemoteFileName = `${aseVideoLessonId}.1080.mp4`;
274
- const aseVideoPath = (0, utils_1.join)(languageId, 'full_lessons', setInfo.setId, aseVideoRemoteFileName);
219
+ if (meetLanguageInfo.languageId === 'ase') {
220
+ const path = `ase/full_lessons/${setInfo.setId}/ase.${lessonId}.1080.mp4`;
275
221
  video = {
276
- id: aseVideoLessonId,
277
- localFileName: aseVideoLocalFileName,
278
- remoteFileName: aseVideoRemoteFileName,
279
- path: aseVideoPath,
280
- url: (0, utils_1.firebasePath)(aseVideoPath),
222
+ id: `ase.${lessonId}`,
223
+ languageId: 'ase',
224
+ path,
225
+ localFileName: `ase.${lessonId}.mp4`,
226
+ remoteFileName: `ase.${lessonId}.1080.mp4`,
227
+ url: exports.firebaseUrl + encodeURIComponent(path) + `?alt=media`,
281
228
  };
282
229
  trainingVideo = undefined;
283
230
  }
284
231
  else {
285
- const videoLessonId = [
286
- languageId,
287
- languageInfo.contentLanguages.trainingVideos,
288
- lessonId,
289
- ].join('.');
290
- const videoLessonRemoteFileName = `${videoLessonId}.1080.mp4`;
291
- const videoLessonLocalFileName = `${videoLessonId}.mp4`;
292
- const videoPath = (0, utils_1.join)(languageId, 'full_lessons', setInfo.setId, videoLessonRemoteFileName);
232
+ const id = `${meetLanguageId}.${meetLanguageInfo.trainingVideoLanguage}.${lessonId}`;
233
+ const remoteFileName = `${id}.1080.mp4`;
234
+ const localFileName = `${id}.mp4`;
235
+ const path = `${meetLanguageId}/full_lessons/${setInfo?.setId}/${remoteFileName}`;
293
236
  video = {
294
- id: videoLessonId,
295
- localFileName: videoLessonLocalFileName,
296
- remoteFileName: videoLessonRemoteFileName,
297
- path: videoPath,
298
- url: (0, utils_1.firebasePath)(videoPath),
237
+ id,
238
+ languageId: meetLanguageInfo.trainingVideoLanguage,
239
+ path,
240
+ localFileName,
241
+ remoteFileName,
242
+ url: exports.firebaseUrl + encodeURIComponent(path) + `?alt=media`,
299
243
  };
300
- const trainingVideoId = [
301
- languageInfo.contentLanguages.trainingVideos,
302
- lessonId,
303
- ].join('.');
244
+ const trainingVideoId = `${meetLanguageInfo.trainingVideoLanguage}.${lessonId}`;
304
245
  const trainingVideoFileName = `${trainingVideoId}.mp4`;
305
246
  // Add the length of the training video to the application duration.
306
247
  applicationDuration +=
307
- mediaDurations_1.mediaDurations[languageInfo.contentLanguages.trainingVideos]?.[trainingVideoFileName] ?? 0;
308
- const trainingVideoPath = (0, utils_1.join)(languageInfo.contentLanguages.trainingVideos, 'course', 'videos_compressed', trainingVideoFileName);
248
+ mediaDurations_1.mediaDurations[meetLanguageInfo.trainingVideoLanguage]?.[trainingVideoFileName] ?? 0;
249
+ const trainingVideoPath = `${meetLanguageInfo.trainingVideoLanguage}/course/videos_compressed/${trainingVideoFileName}`;
309
250
  trainingVideo = {
310
251
  id: trainingVideoId,
311
- localFileName: trainingVideoFileName,
312
- remoteFileName: trainingVideoFileName,
252
+ languageId: meetLanguageInfo.trainingVideoLanguage,
313
253
  path: trainingVideoPath,
314
- url: (0, utils_1.firebasePath)(trainingVideoPath),
254
+ localFileName: trainingVideoFileName,
255
+ remoteFileName,
256
+ url: exports.firebaseUrl + encodeURIComponent(trainingVideoPath) + `?alt=media`,
315
257
  };
316
258
  }
317
259
  let youtubeLink;
@@ -329,41 +271,33 @@ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions,
329
271
  video,
330
272
  trainingVideo,
331
273
  youtubeLink,
332
- passagesString,
274
+ passagesString: (0, scripturePassages_1.getPassagesString)(lesson.s, meetLanguageInfo),
333
275
  };
334
276
  }
335
277
  else {
336
- const assetVersion = languageInfo.audioAssetVersion ?? '';
337
- const fullLessonId = `${languageId}.${lessonId}`;
338
- const fullLessonFileName = `${fullLessonId}.mp3`;
339
- const fullLessonPath = (0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, fullLessonFileName);
340
- const storyId = fullLessonId + '.story';
341
- const storyFileName = `${storyId}.mp3`;
342
- const storyPath = (0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, storyFileName);
343
278
  return {
344
279
  type: 'dbs',
345
280
  ...baseInfo,
346
281
  fellowshipDuration,
347
282
  applicationDuration,
348
- passagesString,
283
+ introLength,
349
284
  full: {
350
- id: fullLessonId,
351
- localFileName: fullLessonFileName,
352
- remoteFileName: fullLessonFileName,
353
- path: fullLessonPath,
354
- url: (0, utils_1.firebasePath)(fullLessonPath),
355
- },
356
- story: {
357
- id: storyId,
358
- localFileName: storyFileName,
359
- remoteFileName: storyFileName,
360
- path: storyPath,
361
- url: (0, utils_1.firebasePath)(storyPath),
285
+ id: `${meetLanguageId}.${lessonId}`,
286
+ localFileName: `${meetLanguageId}.${lessonId}.mp3`,
287
+ remoteFileName: `${meetLanguageId}.${lessonId}.mp3`,
288
+ languageId: meetLanguageId,
289
+ path: `${meetLanguageId}/full_lessons${meetLanguageInfo.audioAssetVersion ?? ''}/${setInfo.setId}/${meetLanguageId}.${lessonId}.mp3`,
290
+ url: exports.firebaseUrl +
291
+ encodeURIComponent(`${meetLanguageId}/full_lessons${meetLanguageInfo.audioAssetVersion ?? ''}/${setInfo.setId}/${meetLanguageId}.${lessonId}.mp3`) +
292
+ `?alt=media`,
362
293
  },
363
294
  dbsCast: {
364
- remoteFileName: `${languageId}.${lessonId}.1080.mp4`,
365
- url: (0, utils_1.firebasePath)((0, utils_1.join)(languageId, `full_lessons${assetVersion}`, setInfo.setId, `${languageId}.${lessonId}.1080.mp4`)),
295
+ remoteFileName: `${meetLanguageId}.${lessonId}.1080.mp4`,
296
+ url: exports.firebaseUrl +
297
+ encodeURIComponent(`${meetLanguageId}/full_lessons/${setInfo.setId}/${meetLanguageId}.${lessonId}.1080.mp4`) +
298
+ `?alt=media`,
366
299
  },
300
+ passagesString: (0, scripturePassages_1.getPassagesString)(lesson.s, meetLanguageInfo),
367
301
  };
368
302
  }
369
303
  }
@@ -414,20 +348,18 @@ function shouldShowLesson(lessonInfo, languageInfo) {
414
348
  return false;
415
349
  }
416
350
  else if (lessonInfo.type === 'eq' &&
417
- mediaDurations_1.mediaDurations[languageInfo.contentLanguages.intros]?.[lessonInfo.eq.remoteFileName] === undefined) {
351
+ !languageAssets_1.languageAssets[languageInfo.introBridge ?? languageInfo.languageId].find((f) => f === lessonInfo.eq.remoteFileName)) {
418
352
  console.log('Missing remote eq file for lesson:', lessonInfo.lessonId);
419
353
  return false;
420
354
  }
421
355
  else if (lessonInfo.type === 'dbs' &&
422
- mediaDurations_1.mediaDurations[lessonInfo.languageId]?.[lessonInfo.full.remoteFileName] ===
423
- undefined) {
356
+ !languageAssets_1.languageAssets[lessonInfo.meetLanguageId].find((f) => f === lessonInfo.full.remoteFileName)) {
424
357
  console.log('Missing remote dbs file for lesson:', lessonInfo.lessonId);
425
358
  return false;
426
359
  }
427
360
  else if (lessonInfo.type === 'video' &&
428
361
  lessonInfo.video &&
429
- mediaDurations_1.mediaDurations[lessonInfo.languageId]?.[lessonInfo.video.remoteFileName] ===
430
- undefined) {
362
+ !languageAssets_1.languageAssets[lessonInfo.meetLanguageId].find((f) => f === lessonInfo.video.remoteFileName)) {
431
363
  console.log('Missing remote video for lesson:', lessonInfo.video.remoteFileName);
432
364
  return false;
433
365
  }
@@ -435,21 +367,101 @@ function shouldShowLesson(lessonInfo, languageInfo) {
435
367
  return true;
436
368
  }
437
369
  /**
438
- * Validates that the computed lesson duration (from section lengths) matches
439
- * the actual audio file duration. Sections must be enriched (all have `length`)
440
- * via {@link enrichSections} before calling this.
441
- *
442
- * @returns The difference between the computed length and the total duration.
370
+ * Calculate start times for each section based on question durations and total
371
+ * audio duration. Must be called after audio is loaded to get accurate total
372
+ * duration.
443
373
  */
444
- function getExpectedLessonDuration({ enrichedSections, lessonInfo, }) {
445
- let computedLength = 0;
446
- for (const section of enrichedSections) {
447
- if (!section.chapter)
374
+ function calculateSectionTimings({ languageInfo, thisLesson, totalDuration, normalizedScripture, supportsVerseHighlight, }) {
375
+ const languageDurations = mediaDurations_1.mediaDurations[languageInfo.languageId];
376
+ const pauses = (0, scripturePassages_1.getLessonPauses)(thisLesson.meetLanguageId);
377
+ const getQuestionDuration = (questionId) => {
378
+ const fileName = `${languageInfo.languageId}.${questionId}.mp3`;
379
+ return languageDurations?.[fileName] ?? 0;
380
+ };
381
+ const timings = new Map();
382
+ // Title section always starts at 0
383
+ timings.set('title', 0);
384
+ // Calculate fellowship question timings from the start
385
+ let currentTime = 0;
386
+ /** Add timings for each fellowship question. */
387
+ thisLesson.sections
388
+ .filter((s) => s.chapter === sets_2.Chapter.FELLOWSHIP)
389
+ .forEach((section) => {
390
+ timings.set(section.id, currentTime);
391
+ currentTime += getQuestionDuration(section.id);
392
+ });
393
+ /** Add the pause before the story chapter starts. */
394
+ currentTime += pauses.beforeStory;
395
+ /** Add the intro timing and pause if we have one. */
396
+ const introSection = thisLesson.sections.find((s) => s.chapter === sets_2.Chapter.INTRODUCTION);
397
+ if (introSection && thisLesson.type === 'dbs' && thisLesson.introLength) {
398
+ timings.set(introSection.id, currentTime);
399
+ currentTime += thisLesson.introLength;
400
+ }
401
+ const storySections = thisLesson.sections.filter((s) => s.chapter === sets_2.Chapter.STORY);
402
+ if (storySections.length > 0) {
403
+ /** First passage will always start right after all previous sections. */
404
+ timings.set(storySections[0].id, currentTime);
405
+ /** Subsequent passages require normalized scripture data to get timings for. */
406
+ if (storySections.length > 1 && supportsVerseHighlight)
407
+ storySections.slice(1).forEach((section) => {
408
+ const passageStartTime = normalizedScripture[section.indexWithinChapter]?.verses?.[0]
409
+ ?.timings?.[0];
410
+ if (passageStartTime != null)
411
+ timings.set(section.id, passageStartTime);
412
+ });
413
+ }
414
+ /**
415
+ * Application section starts after story ends, but we can work backwards and
416
+ * calculate its start time by subtracting its duration from the total
417
+ * duration. This way, if we don't have verse timings data, we can still have
418
+ * timings for the application section.
419
+ */
420
+ const applicationStartTime = totalDuration - thisLesson.applicationDuration;
421
+ currentTime = applicationStartTime;
422
+ thisLesson.sections
423
+ .filter((s) => s.chapter === sets_2.Chapter.APPLICATION)
424
+ .forEach((section) => {
425
+ timings.set(section.id, currentTime);
426
+ if (section.id.includes('video') &&
427
+ thisLesson.type === 'video' &&
428
+ thisLesson.trainingVideo)
429
+ currentTime +=
430
+ mediaDurations_1.mediaDurations[thisLesson.trainingVideo.languageId]?.[thisLesson.trainingVideo.localFileName] ?? 0;
431
+ else
432
+ currentTime += getQuestionDuration(section.id);
433
+ });
434
+ return timings;
435
+ }
436
+ /**
437
+ * Checks if pre-normalized verse timings for given scripture are valid. Does
438
+ * this by comparing the real duration of the full lesson mp3 file with the
439
+ * calculated duration based on individual question durations and verse timing
440
+ * data from bible chapter documents. For lessons generated programmatically
441
+ * with recent systems, they should be nearly identical. For older lessons that
442
+ * were made by hand, they may be off. If they're too off, they will be flagged
443
+ * as invalid and the lesson will default to not using verse timings.
444
+ */
445
+ function getSupportsVerseHighlight({ lessonInfo, scripture, totalDuration, }) {
446
+ if (!totalDuration)
447
+ return { computedLength: 0, diff: 0, passed: false };
448
+ let lastEnd;
449
+ for (const passage of scripture) {
450
+ if (!passage)
448
451
  continue;
449
- computedLength += section.pauseBefore ?? 0;
450
- if (section.chapter === sets_2.Chapter.STORY)
451
- computedLength += (0, scripturePassages_1.getFtbDuration)(section, lessonInfo);
452
- computedLength += section.length ?? 0;
452
+ for (const verse of passage.verses) {
453
+ if (verse.timings) {
454
+ if (lastEnd === undefined || verse.timings[1] > lastEnd) {
455
+ lastEnd = verse.timings[1];
456
+ }
457
+ }
458
+ }
453
459
  }
454
- return computedLength;
460
+ if (lastEnd === undefined)
461
+ return { computedLength: 0, diff: 0, passed: false };
462
+ const computedLength = lastEnd +
463
+ (0, scripturePassages_1.getLessonPauses)(lessonInfo.meetLanguageId).afterStory +
464
+ lessonInfo.applicationDuration;
465
+ const diff = Math.abs(computedLength - totalDuration);
466
+ return { computedLength, diff, passed: diff < 3 };
455
467
  }
@@ -9,7 +9,7 @@ export type BibleVerse = z.infer<typeof BibleVerse>;
9
9
  export declare const BibleChapter: z.ZodObject<{
10
10
  bookName: z.ZodString;
11
11
  chapterId: z.ZodString;
12
- reference: z.ZodNullable<z.ZodOptional<z.ZodString>>;
12
+ reference: z.ZodOptional<z.ZodString>;
13
13
  titles: z.ZodOptional<z.ZodUnion<readonly [z.ZodRecord<z.ZodString, z.ZodString>, z.ZodNull]>>;
14
14
  translationId: z.ZodString;
15
15
  verses: z.ZodArray<z.ZodObject<{
@@ -23,7 +23,7 @@ export type BibleChapter = z.infer<typeof BibleChapter>;
23
23
  export declare const BibleChapters: z.ZodArray<z.ZodObject<{
24
24
  bookName: z.ZodString;
25
25
  chapterId: z.ZodString;
26
- reference: z.ZodNullable<z.ZodOptional<z.ZodString>>;
26
+ reference: z.ZodOptional<z.ZodString>;
27
27
  titles: z.ZodOptional<z.ZodUnion<readonly [z.ZodRecord<z.ZodString, z.ZodString>, z.ZodNull]>>;
28
28
  translationId: z.ZodString;
29
29
  verses: z.ZodArray<z.ZodObject<{
@@ -18,7 +18,7 @@ exports.BibleChapter = zod_1.z
18
18
  chapterId: zod_1.z
19
19
  .string()
20
20
  .regex(new RegExp('^(GEN|EXO|LEV|NUM|DEU|JOS|JDG|RUT|1SA|2SA|1KI|2KI|1CH|2CH|EZR|NEH|EST|JOB|PSA|PRO|ECC|SNG|ISA|JER|LAM|EZK|DAN|HOS|JOL|AMO|OBA|JON|MIC|NAM|HAB|ZEP|HAG|ZEC|MAL|MAT|MRK|LUK|JHN|ACT|ROM|1CO|2CO|GAL|EPH|PHP|COL|1TH|2TH|1TI|2TI|TIT|PHM|HEB|JAS|1PE|2PE|1JN|2JN|3JN|JUD|REV)\\.[0-9]+$')),
21
- reference: zod_1.z.string().optional().nullable(),
21
+ reference: zod_1.z.string().optional(),
22
22
  titles: zod_1.z.union([zod_1.z.record(zod_1.z.string(), zod_1.z.string()), zod_1.z.null()]).optional(),
23
23
  translationId: zod_1.z.string(),
24
24
  verses: zod_1.z.array(exports.BibleVerse).min(1),