waha-shared 1.0.308 → 1.0.310

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 (110) hide show
  1. package/dist/data/areas/areas.json +50 -48
  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 +597 -314
  7. package/dist/data/bibleAudios/bibleAudios.schema.json +17 -8
  8. package/dist/data/bibleAudios/bibleAudios.zod.d.ts +43 -2
  9. package/dist/data/bibleAudios/bibleAudios.zod.js +16 -7
  10. package/dist/data/bibleAudios/index.d.ts +2 -1
  11. package/dist/data/bibleBooks/bibleBooks.json +792 -790
  12. package/dist/data/bibleStatuses/bibleStatuses.json +8302 -8711
  13. package/dist/data/bibleStatuses/bibleStatuses.schema.json +6 -0
  14. package/dist/data/bibleStatuses/bibleStatuses.zod.d.ts +1 -0
  15. package/dist/data/bibleStatuses/bibleStatuses.zod.js +4 -0
  16. package/dist/data/bibleStatuses/index.d.ts +1 -0
  17. package/dist/data/bibleTexts/bibleTexts.json +285 -187
  18. package/dist/data/bibleTexts/bibleTexts.schema.json +7 -0
  19. package/dist/data/bibleTexts/bibleTexts.zod.d.ts +28 -0
  20. package/dist/data/bibleTexts/bibleTexts.zod.js +10 -3
  21. package/dist/data/bibleTexts/index.d.ts +2 -0
  22. package/dist/data/clones/clones.json +2 -0
  23. package/dist/data/countries/countries.json +480 -480
  24. package/dist/data/countriesAndLanguages/countriesAndLanguages.json +6812 -6810
  25. package/dist/data/crowdinLanguages/crowdinLanguages.json +1440 -1438
  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 +86 -69
  31. package/dist/data/curriculumOnboarding/curriculumOnboarding.json +34 -0
  32. package/dist/data/curriculumOnboarding/curriculumOnboarding.schema.json +38 -0
  33. package/dist/data/curriculumOnboarding/curriculumOnboarding.zod.d.ts +11 -0
  34. package/dist/data/curriculumOnboarding/curriculumOnboarding.zod.js +18 -0
  35. package/dist/data/curriculumOnboarding/index.d.ts +9 -0
  36. package/dist/data/curriculumOnboarding/index.js +7 -0
  37. package/dist/data/curriculumQuestions/curriculumQuestions.json +194 -192
  38. package/dist/data/curriculumTopics/curriculumTopics.json +51 -8
  39. package/dist/data/dblAudioLicenses/dblAudioLicenses.json +450 -421
  40. package/dist/data/dblTextLicenses/dblTextLicenses.json +1251 -1161
  41. package/dist/data/firebase.d.ts +1 -0
  42. package/dist/data/firebase.js +4 -0
  43. package/dist/data/iosVoiceOverLanguages/iosVoiceOverLanguages.json +1 -1
  44. package/dist/data/iso6933LanguageCodes/iso6933LanguageCodes.json +1 -1
  45. package/dist/data/languages/index.d.ts +22 -10
  46. package/dist/data/languages/languages.json +3299 -2012
  47. package/dist/data/languages/languages.schema.json +102 -48
  48. package/dist/data/languages/languages.zod.d.ts +96 -30
  49. package/dist/data/languages/languages.zod.js +66 -37
  50. package/dist/data/lessonPauses/index.d.ts +0 -1
  51. package/dist/data/lessonPauses/lessonPauses.json +2 -3
  52. package/dist/data/lessonPauses/lessonPauses.schema.json +1 -11
  53. package/dist/data/lessonPauses/lessonPauses.zod.d.ts +0 -1
  54. package/dist/data/lessonPauses/lessonPauses.zod.js +0 -3
  55. package/dist/data/mediaDurations/mediaDurations.json +35797 -6076
  56. package/dist/data/mediaDurations/mediaDurations.schema.json +1 -1
  57. package/dist/data/mediaDurations/mediaDurations.zod.js +1 -1
  58. package/dist/data/notification/index.d.ts +1 -1
  59. package/dist/data/notification/notification.json +93 -169
  60. package/dist/data/phoneLanguages/phoneLanguages.json +637 -635
  61. package/dist/data/questions/questions.json +149 -147
  62. package/dist/data/releaseNotes/releaseNotes.json +176 -123
  63. package/dist/data/screenshots/screenshots.json +1 -1
  64. package/dist/data/scripts/scripts.json +6 -1
  65. package/dist/data/sets/index.d.ts +3 -3
  66. package/dist/data/sets/sets.json +1770 -480
  67. package/dist/data/sets/sets.schema.json +1 -1
  68. package/dist/data/sets/sets.zod.d.ts +3 -3
  69. package/dist/data/sets/sets.zod.js +3 -6
  70. package/dist/data/translationsApp/index.d.ts +1 -0
  71. package/dist/data/translationsApp/translationsApp.json +1610 -1569
  72. package/dist/data/translationsApp/translationsApp.schema.json +3 -1
  73. package/dist/data/translationsApp/translationsApp.zod.d.ts +2 -0
  74. package/dist/data/translationsApp/translationsApp.zod.js +1 -0
  75. package/dist/data/translationsFtb/translationsFtb.json +120 -664
  76. package/dist/data/translationsIntroduction/translationsIntroduction.json +1 -1
  77. package/dist/data/translationsQuestion/translationsQuestion.json +65 -33
  78. package/dist/data/translationsSet/translationsSet.json +30139 -19402
  79. package/dist/data/translationsSet/translationsSet.schema.json +3 -4
  80. package/dist/data/translationsSet/translationsSet.zod.js +4 -4
  81. package/dist/data/translationsSolarSpeaker/translationsSolarSpeaker.json +1 -1
  82. package/dist/data/translationsSpokenQuestion/translationsSpokenQuestion.json +65 -33
  83. package/dist/data/youtubePlaylists/youtubePlaylists.json +10 -10
  84. package/dist/data/youtubeVideos/youtubeVideos.json +82 -82
  85. package/dist/functions/bibleBooks.d.ts +16 -0
  86. package/dist/functions/bibleBooks.js +101 -0
  87. package/dist/functions/bibles.d.ts +47 -0
  88. package/dist/functions/bibles.js +315 -0
  89. package/dist/functions/languages.d.ts +6 -7
  90. package/dist/functions/languages.js +58 -53
  91. package/dist/functions/scripturePassages.d.ts +53 -47
  92. package/dist/functions/scripturePassages.js +217 -303
  93. package/dist/functions/sets.d.ts +13 -52
  94. package/dist/functions/sets.js +195 -205
  95. package/dist/functions/utils.d.ts +2 -0
  96. package/dist/functions/utils.js +8 -0
  97. package/dist/types/bibleChapters.d.ts +2 -2
  98. package/dist/types/bibleChapters.js +1 -1
  99. package/dist/types/languages.d.ts +48 -9
  100. package/dist/types/languages.js +2 -0
  101. package/dist/types/scripturePassages.d.ts +0 -1
  102. package/dist/types/scripturePassages.js +0 -1
  103. package/dist/types/sets.d.ts +28 -11
  104. package/package.json +1 -1
  105. package/dist/data/languageAssets/index.d.ts +0 -1
  106. package/dist/data/languageAssets/index.js +0 -7
  107. package/dist/data/languageAssets/languageAssets.json +0 -45406
  108. package/dist/data/languageAssets/languageAssets.schema.json +0 -19
  109. package/dist/data/languageAssets/languageAssets.zod.d.ts +0 -3
  110. package/dist/data/languageAssets/languageAssets.zod.js +0 -7
@@ -1,22 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.firebaseUrl = void 0;
4
3
  exports.getSetInfo = getSetInfo;
5
4
  exports.getLessonInfo = getLessonInfo;
6
5
  exports.convertSToString = convertSToString;
7
6
  exports.shouldShowLesson = shouldShowLesson;
8
- exports.calculateSectionTimings = calculateSectionTimings;
9
- exports.getSupportsVerseHighlight = getSupportsVerseHighlight;
10
- const languageAssets_1 = require("../data/languageAssets");
7
+ exports.getExpectedLessonDuration = getExpectedLessonDuration;
8
+ const bibleStatuses_1 = require("../data/bibleStatuses");
11
9
  const mediaDurations_1 = require("../data/mediaDurations");
12
- const questions_1 = require("../data/questions");
13
10
  const sets_1 = require("../data/sets");
14
11
  const specialIds_1 = require("../data/specialIds");
15
12
  const youtubeVideos_1 = require("../data/youtubeVideos");
16
13
  const scripturePassages_1 = require("../functions/scripturePassages");
17
14
  const sets_2 = require("../types/sets");
18
- exports.firebaseUrl = 'https://firebasestorage.googleapis.com/v0/b/waha-app-db.appspot.com/o/';
19
- function getSetInfo({ setId, meetLanguageId, setIds, t, }) {
15
+ const bibles_1 = require("./bibles");
16
+ const utils_1 = require("./utils");
17
+ function getSetInfo({ setId, languageId, setIds, t, }) {
20
18
  const idComponents = setId.split('.');
21
19
  const category = idComponents[0] === '01'
22
20
  ? 'Foundations'
@@ -30,7 +28,7 @@ function getSetInfo({ setId, meetLanguageId, setIds, t, }) {
30
28
  const thisSet = sets_1.sets.find((set) => set.setId === setId);
31
29
  if (!thisSet)
32
30
  return;
33
- if (meetLanguageId === 'ase') {
31
+ if (languageId === 'ase') {
34
32
  const englishEquivalent = sets_1.sets.find((set) => set.name === thisSet.name && !set.comment);
35
33
  thisSetTranslations = t.sets.find((setTranslations) => setTranslations.setId === englishEquivalent?.setId);
36
34
  }
@@ -48,26 +46,26 @@ function getSetInfo({ setId, meetLanguageId, setIds, t, }) {
48
46
  setSubtitle: category === 'Topics'
49
47
  ? (thisSetTranslations?.setTag ?? '')
50
48
  : `${categoryName} ${parseInt(setNumber)}`,
51
- setLink: `https://web.waha.app/set?set-id=${setId}&meet-language=${meetLanguageId}`,
52
- meetLanguageId,
49
+ setLink: `https://web.waha.app/set?set-id=${setId}&meet-language=${languageId}`,
50
+ languageId,
53
51
  setNumber,
54
52
  category,
55
53
  };
56
54
  return toReturn;
57
55
  }
58
- function getLessonInfo({ lessonId, meetLanguageInfo, setInfo, t, useSpokenQuestions, }) {
56
+ function getLessonInfo({ lessonId, languageInfo, setInfo, t, useSpokenQuestions, }) {
59
57
  if (!lessonId)
60
58
  return;
61
59
  else if (lessonId.split('.').length !== 3)
62
60
  return;
63
61
  const [category, setNumber, lessonNumber] = lessonId.split('.');
64
- const meetLanguageId = meetLanguageInfo.languageId;
62
+ const languageId = languageInfo.languageId;
65
63
  setInfo =
66
64
  setInfo ??
67
65
  getSetInfo({
68
66
  setId: [category, setNumber].join('.'),
69
- meetLanguageId,
70
- setIds: meetLanguageInfo.setIds,
67
+ languageId,
68
+ setIds: languageInfo.setIds,
71
69
  t,
72
70
  });
73
71
  if (!setInfo) {
@@ -79,7 +77,7 @@ function getLessonInfo({ lessonId, meetLanguageInfo, setInfo, t, useSpokenQuesti
79
77
  return;
80
78
  let thisSetTranslations;
81
79
  let lessonTitle;
82
- if (meetLanguageId === 'ase') {
80
+ if (languageId === 'ase') {
83
81
  const englishEquivalent = sets_1.sets.find((set) => set.name === setInfo.name && !set.comment);
84
82
  thisSetTranslations = t.sets.find((setTranslations) => setTranslations.setId === englishEquivalent?.setId);
85
83
  lessonTitle =
@@ -89,171 +87,233 @@ function getLessonInfo({ lessonId, meetLanguageInfo, setInfo, t, useSpokenQuesti
89
87
  thisSetTranslations = t.sets.find((setTranslations) => setTranslations.setId === setInfo?.setId);
90
88
  lessonTitle = thisSetTranslations?.lessonTitles?.[lessonId];
91
89
  }
92
- const fellowshipQuestions = meetLanguageInfo.questionSets.find((questionSet) => questionSet.questionSetId === lesson.f)?.questions ?? [];
90
+ const fellowshipQuestions = languageInfo.questionSets.find((questionSet) => questionSet.questionSetId === lesson.f)?.questions ?? [];
93
91
  const fellowshipQuestionsText = fellowshipQuestions.map((questionId) => useSpokenQuestions ? t.questionsSpoken[questionId] : t.questions[questionId]);
94
- const applicationQuestions = meetLanguageInfo.questionSets.find((questionSet) => questionSet.questionSetId === lesson.a)?.questions ?? [];
92
+ const applicationQuestions = languageInfo.questionSets.find((questionSet) => questionSet.questionSetId === lesson.a)?.questions ?? [];
95
93
  const applicationQuestionsText = applicationQuestions.map((questionId) => useSpokenQuestions ? t.questionsSpoken[questionId] : t.questions[questionId]);
96
94
  const sections = [
97
95
  {
98
96
  id: 'title',
99
- isChapter: false,
100
- header: lessonTitle ?? '',
101
97
  index: 0,
102
- chapter: null,
103
98
  indexWithinChapter: 0,
104
- text: lessonTitle ?? '',
105
- hasBeep: false,
99
+ fileName: '',
100
+ languageId,
106
101
  },
107
102
  ];
108
103
  fellowshipQuestions.forEach((questionId, index) => {
109
- const hasBeep = questions_1.questions.find((question) => question.questionId === questionId)
110
- ?.hasBeep ?? false;
104
+ const fileName = `${languageInfo.contentLanguages.questionAudio}.${questionId}.mp3`;
111
105
  sections.push({
112
106
  id: questionId,
113
- isChapter: index === 0,
107
+ languageId: languageInfo.contentLanguages.questionAudio,
114
108
  index: sections.length,
115
- header: index === 0 ? t.fellowship : null,
109
+ header: index === 0 ? t.fellowship : undefined,
116
110
  chapter: sets_2.Chapter.FELLOWSHIP,
117
111
  indexWithinChapter: index,
118
112
  text: fellowshipQuestionsText[index],
119
- hasBeep,
113
+ fileName,
120
114
  });
121
115
  });
122
- let introLength = undefined;
123
116
  if (specialIds_1.specialIds.evaluationQuestionLessonIds.includes(lesson.lessonId)) {
124
117
  sections.push({
125
118
  id: 'eq',
126
- isChapter: false,
119
+ languageId: languageInfo.contentLanguages.intros,
127
120
  index: sections.length,
128
121
  chapter: sets_2.Chapter.INTRODUCTION,
129
122
  indexWithinChapter: 0,
130
- header: null,
123
+ pauseBefore: 0,
131
124
  text: t.introductions.introductions.eq,
132
- hasBeep: false,
125
+ fileName: `${languageInfo.contentLanguages.intros}.eq.mp3`,
133
126
  });
134
127
  }
135
128
  else if (specialIds_1.specialIds.growingAsDmcSetIds.includes(setInfo.setId)) {
129
+ const introFileName = [
130
+ languageInfo.contentLanguages.intros,
131
+ lessonId,
132
+ 'intro',
133
+ 'mp3',
134
+ ].join('.');
136
135
  sections.push({
137
136
  id: 'introduction',
138
- isChapter: false,
137
+ languageId: languageInfo.contentLanguages.intros,
139
138
  index: sections.length,
140
139
  chapter: sets_2.Chapter.INTRODUCTION,
141
140
  indexWithinChapter: 0,
141
+ pauseBefore: languageInfo.lessonPauses.beforeFtb,
142
142
  header: t.introductions.introduction,
143
143
  text: t.introductions.introductions[lessonId],
144
- hasBeep: false,
144
+ fileName: introFileName,
145
145
  });
146
- const introLanguageId = meetLanguageInfo.introBridge ?? meetLanguageId;
147
- introLength =
148
- mediaDurations_1.mediaDurations[introLanguageId]?.[`${introLanguageId}.${lessonId}.mp3`];
149
146
  }
150
- lesson.s?.forEach((passageId, index) => {
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;
151
156
  sections.push({
152
- id: passageId,
153
- isChapter: index === 0,
154
- hasBeep: false,
157
+ id: `${bibleId};${passageId}`,
158
+ languageId,
155
159
  index: sections.length,
156
160
  chapter: sets_2.Chapter.STORY,
157
161
  indexWithinChapter: index,
158
- text: '',
159
- header: (0, scripturePassages_1.getPassagesString)([passageId], meetLanguageInfo),
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
+ header: (0, scripturePassages_1.getPassagesString)([{ bibleId, passageId }]),
172
+ });
173
+ lastBook = passageInfo.book;
174
+ });
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
+ let concatStoryIndex = lesson.s.length;
182
+ languageInfo.bibleModifiers?.forEach((modifier) => {
183
+ lesson.s.forEach((passageId) => {
184
+ if (!(0, bibles_1.shouldConcatBibleWith)({
185
+ modifier,
186
+ lessonCategory: setInfo.category,
187
+ passageId,
188
+ }))
189
+ return;
190
+ const passageInfo = (0, scripturePassages_1.parseVerseRange)(passageId);
191
+ const ftbRequired = lastBook !== passageInfo.book;
192
+ sections.push({
193
+ id: `${modifier.bibleId};${passageId}`,
194
+ languageId,
195
+ index: sections.length,
196
+ chapter: sets_2.Chapter.STORY,
197
+ indexWithinChapter: concatStoryIndex,
198
+ fileName: passageInfo.chapterAudioFileName,
199
+ pauseBefore: ftbRequired
200
+ ? languageInfo.lessonPauses.beforeFtb
201
+ : languageInfo.lessonPauses.betweenPassages,
202
+ ftbFileName: ftbRequired
203
+ ? `${languageInfo.contentLanguages.ftbs}.${passageInfo.book}.mp3`
204
+ : undefined,
205
+ header: (0, scripturePassages_1.getPassagesString)([{ bibleId: modifier.bibleId, passageId }]),
206
+ });
207
+ lastBook = passageInfo.book;
208
+ concatStoryIndex++;
160
209
  });
161
210
  });
162
211
  applicationQuestions.forEach((questionId, index) => {
163
- const hasBeep = questions_1.questions.find((question) => question.questionId === questionId)
164
- ?.hasBeep ?? false;
212
+ const fileName = questionId.includes('video')
213
+ ? `${languageInfo.contentLanguages.trainingVideos}.${lessonId}.mp4`
214
+ : `${languageInfo.contentLanguages.questionAudio}.${questionId}.mp3`;
165
215
  sections.push({
166
216
  id: questionId,
167
- isChapter: index === 0,
168
- hasBeep,
217
+ languageId: questionId.includes('video')
218
+ ? languageInfo.contentLanguages.trainingVideos
219
+ : languageInfo.contentLanguages.questionAudio,
169
220
  index: sections.length,
170
221
  chapter: sets_2.Chapter.APPLICATION,
171
222
  indexWithinChapter: index,
172
- header: index === 0 ? t.application : null,
223
+ pauseBefore: index === 0 ? languageInfo.lessonPauses.afterStory : undefined,
224
+ header: index === 0 ? t.application : undefined,
173
225
  text: questionId.includes('video')
174
226
  ? t.training_video
175
227
  : applicationQuestionsText[index],
228
+ fileName,
176
229
  });
177
230
  });
178
231
  const baseInfo = {
179
- lessonLink: `https://web.waha.app/lesson?lesson-id=${lessonId}&meet-language=${meetLanguageId}`,
232
+ lessonLink: `https://web.waha.app/lesson?lesson-id=${lessonId}&meet-language=${languageId}`,
180
233
  lessonNumber,
181
234
  lessonTitle,
182
235
  sections,
183
236
  ...setInfo,
184
237
  ...lesson,
238
+ ...languageInfo,
185
239
  };
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);
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);
195
250
  if (specialIds_1.specialIds.evaluationQuestionLessonIds.includes(lesson.lessonId)) {
196
- const languageId = meetLanguageInfo.introBridge ?? meetLanguageId;
197
- const id = `${languageId}.eq`;
198
- const fileName = `${id}.mp3`;
199
- const path = `${languageId}/introductions/${fileName}`;
251
+ const eqId = `${languageInfo.contentLanguages.intros}.eq`;
252
+ const eqFileName = `${eqId}.mp3`;
253
+ const eqPath = (0, utils_1.join)(languageInfo.contentLanguages.intros, 'introductions', eqFileName);
200
254
  return {
201
255
  type: 'eq',
202
256
  ...baseInfo,
203
257
  fellowshipDuration,
204
258
  applicationDuration,
205
259
  eq: {
206
- id,
207
- languageId,
208
- localFileName: fileName,
209
- remoteFileName: fileName,
210
- path,
211
- url: exports.firebaseUrl + encodeURIComponent(path) + `?alt=media`,
260
+ id: eqId,
261
+ localFileName: eqFileName,
262
+ remoteFileName: eqFileName,
263
+ path: eqPath,
264
+ url: (0, utils_1.firebasePath)(eqPath),
212
265
  },
213
266
  };
214
267
  }
215
268
  else if (setInfo.setId === specialIds_1.specialIds.dmCourseSetId ||
216
- meetLanguageId === 'ase') {
269
+ languageId === 'ase') {
217
270
  let video;
218
271
  let trainingVideo;
219
- if (meetLanguageInfo.languageId === 'ase') {
220
- const path = `ase/full_lessons/${setInfo.setId}/ase.${lessonId}.1080.mp4`;
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);
221
277
  video = {
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`,
278
+ id: aseVideoLessonId,
279
+ localFileName: aseVideoLocalFileName,
280
+ remoteFileName: aseVideoRemoteFileName,
281
+ path: aseVideoPath,
282
+ url: (0, utils_1.firebasePath)(aseVideoPath),
228
283
  };
229
284
  trainingVideo = undefined;
230
285
  }
231
286
  else {
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}`;
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);
236
295
  video = {
237
- id,
238
- languageId: meetLanguageInfo.trainingVideoLanguage,
239
- path,
240
- localFileName,
241
- remoteFileName,
242
- url: exports.firebaseUrl + encodeURIComponent(path) + `?alt=media`,
296
+ id: videoLessonId,
297
+ localFileName: videoLessonLocalFileName,
298
+ remoteFileName: videoLessonRemoteFileName,
299
+ path: videoPath,
300
+ url: (0, utils_1.firebasePath)(videoPath),
243
301
  };
244
- const trainingVideoId = `${meetLanguageInfo.trainingVideoLanguage}.${lessonId}`;
302
+ const trainingVideoId = [
303
+ languageInfo.contentLanguages.trainingVideos,
304
+ lessonId,
305
+ ].join('.');
245
306
  const trainingVideoFileName = `${trainingVideoId}.mp4`;
246
307
  // Add the length of the training video to the application duration.
247
308
  applicationDuration +=
248
- mediaDurations_1.mediaDurations[meetLanguageInfo.trainingVideoLanguage]?.[trainingVideoFileName] ?? 0;
249
- const trainingVideoPath = `${meetLanguageInfo.trainingVideoLanguage}/course/videos_compressed/${trainingVideoFileName}`;
309
+ mediaDurations_1.mediaDurations[languageInfo.contentLanguages.trainingVideos]?.[trainingVideoFileName] ?? 0;
310
+ const trainingVideoPath = (0, utils_1.join)(languageInfo.contentLanguages.trainingVideos, 'course', 'videos_compressed', trainingVideoFileName);
250
311
  trainingVideo = {
251
312
  id: trainingVideoId,
252
- languageId: meetLanguageInfo.trainingVideoLanguage,
253
- path: trainingVideoPath,
254
313
  localFileName: trainingVideoFileName,
255
- remoteFileName,
256
- url: exports.firebaseUrl + encodeURIComponent(trainingVideoPath) + `?alt=media`,
314
+ remoteFileName: trainingVideoFileName,
315
+ path: trainingVideoPath,
316
+ url: (0, utils_1.firebasePath)(trainingVideoPath),
257
317
  };
258
318
  }
259
319
  let youtubeLink;
@@ -271,33 +331,41 @@ function getLessonInfo({ lessonId, meetLanguageInfo, setInfo, t, useSpokenQuesti
271
331
  video,
272
332
  trainingVideo,
273
333
  youtubeLink,
274
- passagesString: (0, scripturePassages_1.getPassagesString)(lesson.s, meetLanguageInfo),
334
+ passagesString,
275
335
  };
276
336
  }
277
337
  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);
278
345
  return {
279
346
  type: 'dbs',
280
347
  ...baseInfo,
281
348
  fellowshipDuration,
282
349
  applicationDuration,
283
- introLength,
350
+ passagesString,
284
351
  full: {
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`,
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),
293
364
  },
294
365
  dbsCast: {
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
+ 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`)),
299
368
  },
300
- passagesString: (0, scripturePassages_1.getPassagesString)(lesson.s, meetLanguageInfo),
301
369
  };
302
370
  }
303
371
  }
@@ -348,18 +416,20 @@ function shouldShowLesson(lessonInfo, languageInfo) {
348
416
  return false;
349
417
  }
350
418
  else if (lessonInfo.type === 'eq' &&
351
- !languageAssets_1.languageAssets[languageInfo.introBridge ?? languageInfo.languageId].find((f) => f === lessonInfo.eq.remoteFileName)) {
419
+ mediaDurations_1.mediaDurations[languageInfo.contentLanguages.intros]?.[lessonInfo.eq.remoteFileName] === undefined) {
352
420
  console.log('Missing remote eq file for lesson:', lessonInfo.lessonId);
353
421
  return false;
354
422
  }
355
423
  else if (lessonInfo.type === 'dbs' &&
356
- !languageAssets_1.languageAssets[lessonInfo.meetLanguageId].find((f) => f === lessonInfo.full.remoteFileName)) {
424
+ mediaDurations_1.mediaDurations[lessonInfo.languageId]?.[lessonInfo.full.remoteFileName] ===
425
+ undefined) {
357
426
  console.log('Missing remote dbs file for lesson:', lessonInfo.lessonId);
358
427
  return false;
359
428
  }
360
429
  else if (lessonInfo.type === 'video' &&
361
430
  lessonInfo.video &&
362
- !languageAssets_1.languageAssets[lessonInfo.meetLanguageId].find((f) => f === lessonInfo.video.remoteFileName)) {
431
+ mediaDurations_1.mediaDurations[lessonInfo.languageId]?.[lessonInfo.video.remoteFileName] ===
432
+ undefined) {
363
433
  console.log('Missing remote video for lesson:', lessonInfo.video.remoteFileName);
364
434
  return false;
365
435
  }
@@ -367,101 +437,21 @@ function shouldShowLesson(lessonInfo, languageInfo) {
367
437
  return true;
368
438
  }
369
439
  /**
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.
440
+ * Validates that the computed lesson duration (from section lengths) matches
441
+ * the actual audio file duration. Sections must be enriched (all have `length`)
442
+ * via {@link enrichSections} before calling this.
443
+ *
444
+ * @returns The difference between the computed length and the total duration.
373
445
  */
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)
446
+ function getExpectedLessonDuration({ enrichedSections, lessonInfo, }) {
447
+ let computedLength = 0;
448
+ for (const section of enrichedSections) {
449
+ if (!section.chapter)
451
450
  continue;
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
- }
451
+ computedLength += section.pauseBefore ?? 0;
452
+ if (section.chapter === sets_2.Chapter.STORY)
453
+ computedLength += (0, scripturePassages_1.getFtbDuration)(section, lessonInfo);
454
+ computedLength += section.length ?? 0;
459
455
  }
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 };
456
+ return computedLength;
467
457
  }
@@ -0,0 +1,2 @@
1
+ export declare const join: (...parts: string[]) => string;
2
+ export declare const firebasePath: (path: string) => string;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.firebasePath = exports.join = void 0;
4
+ const firebase_1 = require("../data/firebase");
5
+ const join = (...parts) => parts.join('/').replace(/\/+/g, '/');
6
+ exports.join = join;
7
+ const firebasePath = (path) => firebase_1.firebaseUrl + encodeURIComponent(path) + `?alt=media`;
8
+ exports.firebasePath = firebasePath;
@@ -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.ZodOptional<z.ZodString>;
12
+ reference: z.ZodNullable<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.ZodOptional<z.ZodString>;
26
+ reference: z.ZodNullable<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(),
21
+ reference: zod_1.z.string().optional().nullable(),
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),