waha-shared 1.0.306 → 1.0.307

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 +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 +581 -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 +7981 -8480
  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 +269 -184
  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/curriculumQuestions/curriculumQuestions.json +194 -192
  32. package/dist/data/curriculumTopics/curriculumTopics.json +51 -8
  33. package/dist/data/dblAudioLicenses/dblAudioLicenses.json +36 -7
  34. package/dist/data/dblTextLicenses/dblTextLicenses.json +4 -4
  35. package/dist/data/firebase.d.ts +1 -0
  36. package/dist/data/firebase.js +4 -0
  37. package/dist/data/iosVoiceOverLanguages/iosVoiceOverLanguages.json +1 -1
  38. package/dist/data/iso6933LanguageCodes/iso6933LanguageCodes.json +1 -1
  39. package/dist/data/languages/index.d.ts +20 -10
  40. package/dist/data/languages/languages.json +2983 -2008
  41. package/dist/data/languages/languages.schema.json +93 -47
  42. package/dist/data/languages/languages.zod.d.ts +93 -30
  43. package/dist/data/languages/languages.zod.js +59 -37
  44. package/dist/data/lessonPauses/index.d.ts +0 -1
  45. package/dist/data/lessonPauses/lessonPauses.json +2 -3
  46. package/dist/data/lessonPauses/lessonPauses.schema.json +1 -11
  47. package/dist/data/lessonPauses/lessonPauses.zod.d.ts +0 -1
  48. package/dist/data/lessonPauses/lessonPauses.zod.js +0 -3
  49. package/dist/data/mediaDurations/mediaDurations.json +35291 -6076
  50. package/dist/data/mediaDurations/mediaDurations.schema.json +1 -1
  51. package/dist/data/mediaDurations/mediaDurations.zod.js +1 -1
  52. package/dist/data/notification/index.d.ts +1 -1
  53. package/dist/data/notification/notification.json +93 -169
  54. package/dist/data/phoneLanguages/phoneLanguages.json +637 -635
  55. package/dist/data/questions/questions.json +149 -147
  56. package/dist/data/releaseNotes/releaseNotes.json +126 -124
  57. package/dist/data/screenshots/screenshots.json +1 -1
  58. package/dist/data/scripts/scripts.json +6 -1
  59. package/dist/data/sets/index.d.ts +3 -3
  60. package/dist/data/sets/sets.json +1770 -480
  61. package/dist/data/sets/sets.schema.json +1 -1
  62. package/dist/data/sets/sets.zod.d.ts +3 -3
  63. package/dist/data/sets/sets.zod.js +3 -6
  64. package/dist/data/translationsApp/index.d.ts +1 -0
  65. package/dist/data/translationsApp/translationsApp.json +1776 -1568
  66. package/dist/data/translationsApp/translationsApp.schema.json +1 -0
  67. package/dist/data/translationsApp/translationsApp.zod.d.ts +2 -0
  68. package/dist/data/translationsApp/translationsApp.zod.js +1 -0
  69. package/dist/data/translationsFtb/translationsFtb.json +120 -664
  70. package/dist/data/translationsIntroduction/translationsIntroduction.json +1 -1
  71. package/dist/data/translationsQuestion/translationsQuestion.json +65 -33
  72. package/dist/data/translationsSet/translationsSet.json +30155 -19418
  73. package/dist/data/translationsSet/translationsSet.schema.json +3 -4
  74. package/dist/data/translationsSet/translationsSet.zod.js +4 -4
  75. package/dist/data/translationsSolarSpeaker/translationsSolarSpeaker.json +1 -1
  76. package/dist/data/translationsSpokenQuestion/translationsSpokenQuestion.json +65 -33
  77. package/dist/data/youtubePlaylists/youtubePlaylists.json +10 -10
  78. package/dist/data/youtubeVideos/youtubeVideos.json +82 -82
  79. package/dist/functions/bibleBooks.d.ts +16 -0
  80. package/dist/functions/bibleBooks.js +101 -0
  81. package/dist/functions/bibles.d.ts +44 -0
  82. package/dist/functions/bibles.js +291 -0
  83. package/dist/functions/languages.d.ts +6 -7
  84. package/dist/functions/languages.js +51 -53
  85. package/dist/functions/scripturePassages.d.ts +34 -49
  86. package/dist/functions/scripturePassages.js +167 -304
  87. package/dist/functions/sets.d.ts +13 -52
  88. package/dist/functions/sets.js +193 -205
  89. package/dist/functions/utils.d.ts +2 -0
  90. package/dist/functions/utils.js +8 -0
  91. package/dist/types/bibleChapters.d.ts +2 -2
  92. package/dist/types/bibleChapters.js +1 -1
  93. package/dist/types/languages.d.ts +39 -9
  94. package/dist/types/languages.js +2 -0
  95. package/dist/types/sets.d.ts +21 -11
  96. package/package.json +1 -1
  97. package/dist/data/languageAssets/index.d.ts +0 -1
  98. package/dist/data/languageAssets/index.js +0 -7
  99. package/dist/data/languageAssets/languageAssets.json +0 -45406
  100. package/dist/data/languageAssets/languageAssets.schema.json +0 -19
  101. package/dist/data/languageAssets/languageAssets.zod.d.ts +0 -3
  102. 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,231 @@ 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
+ });
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++;
160
207
  });
161
208
  });
162
209
  applicationQuestions.forEach((questionId, index) => {
163
- const hasBeep = questions_1.questions.find((question) => question.questionId === questionId)
164
- ?.hasBeep ?? false;
210
+ const fileName = questionId.includes('video')
211
+ ? `${languageInfo.contentLanguages.trainingVideos}.${lessonId}.mp3`
212
+ : `${languageInfo.contentLanguages.questionAudio}.${questionId}.mp3`;
165
213
  sections.push({
166
214
  id: questionId,
167
- isChapter: index === 0,
168
- hasBeep,
215
+ languageId: questionId.includes('video')
216
+ ? languageInfo.contentLanguages.trainingVideos
217
+ : languageInfo.contentLanguages.questionAudio,
169
218
  index: sections.length,
170
219
  chapter: sets_2.Chapter.APPLICATION,
171
220
  indexWithinChapter: index,
172
- header: index === 0 ? t.application : null,
221
+ pauseBefore: index === 0 ? languageInfo.lessonPauses.afterStory : undefined,
222
+ header: index === 0 ? t.application : undefined,
173
223
  text: questionId.includes('video')
174
224
  ? t.training_video
175
225
  : applicationQuestionsText[index],
226
+ fileName,
176
227
  });
177
228
  });
178
229
  const baseInfo = {
179
- lessonLink: `https://web.waha.app/lesson?lesson-id=${lessonId}&meet-language=${meetLanguageId}`,
230
+ lessonLink: `https://web.waha.app/lesson?lesson-id=${lessonId}&meet-language=${languageId}`,
180
231
  lessonNumber,
181
232
  lessonTitle,
182
233
  sections,
183
234
  ...setInfo,
184
235
  ...lesson,
236
+ ...languageInfo,
185
237
  };
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);
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);
195
248
  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}`;
249
+ const eqId = `${languageInfo.contentLanguages.intros}.eq`;
250
+ const eqFileName = `${eqId}.mp3`;
251
+ const eqPath = (0, utils_1.join)(languageInfo.contentLanguages.intros, 'introductions', eqFileName);
200
252
  return {
201
253
  type: 'eq',
202
254
  ...baseInfo,
203
255
  fellowshipDuration,
204
256
  applicationDuration,
205
257
  eq: {
206
- id,
207
- languageId,
208
- localFileName: fileName,
209
- remoteFileName: fileName,
210
- path,
211
- url: exports.firebaseUrl + encodeURIComponent(path) + `?alt=media`,
258
+ id: eqId,
259
+ localFileName: eqFileName,
260
+ remoteFileName: eqFileName,
261
+ path: eqPath,
262
+ url: (0, utils_1.firebasePath)(eqPath),
212
263
  },
213
264
  };
214
265
  }
215
266
  else if (setInfo.setId === specialIds_1.specialIds.dmCourseSetId ||
216
- meetLanguageId === 'ase') {
267
+ languageId === 'ase') {
217
268
  let video;
218
269
  let trainingVideo;
219
- if (meetLanguageInfo.languageId === 'ase') {
220
- const path = `ase/full_lessons/${setInfo.setId}/ase.${lessonId}.1080.mp4`;
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);
221
275
  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`,
276
+ id: aseVideoLessonId,
277
+ localFileName: aseVideoLocalFileName,
278
+ remoteFileName: aseVideoRemoteFileName,
279
+ path: aseVideoPath,
280
+ url: (0, utils_1.firebasePath)(aseVideoPath),
228
281
  };
229
282
  trainingVideo = undefined;
230
283
  }
231
284
  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}`;
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);
236
293
  video = {
237
- id,
238
- languageId: meetLanguageInfo.trainingVideoLanguage,
239
- path,
240
- localFileName,
241
- remoteFileName,
242
- url: exports.firebaseUrl + encodeURIComponent(path) + `?alt=media`,
294
+ id: videoLessonId,
295
+ localFileName: videoLessonLocalFileName,
296
+ remoteFileName: videoLessonRemoteFileName,
297
+ path: videoPath,
298
+ url: (0, utils_1.firebasePath)(videoPath),
243
299
  };
244
- const trainingVideoId = `${meetLanguageInfo.trainingVideoLanguage}.${lessonId}`;
300
+ const trainingVideoId = [
301
+ languageInfo.contentLanguages.trainingVideos,
302
+ lessonId,
303
+ ].join('.');
245
304
  const trainingVideoFileName = `${trainingVideoId}.mp4`;
246
305
  // Add the length of the training video to the application duration.
247
306
  applicationDuration +=
248
- mediaDurations_1.mediaDurations[meetLanguageInfo.trainingVideoLanguage]?.[trainingVideoFileName] ?? 0;
249
- const trainingVideoPath = `${meetLanguageInfo.trainingVideoLanguage}/course/videos_compressed/${trainingVideoFileName}`;
307
+ mediaDurations_1.mediaDurations[languageInfo.contentLanguages.trainingVideos]?.[trainingVideoFileName] ?? 0;
308
+ const trainingVideoPath = (0, utils_1.join)(languageInfo.contentLanguages.trainingVideos, 'course', 'videos_compressed', trainingVideoFileName);
250
309
  trainingVideo = {
251
310
  id: trainingVideoId,
252
- languageId: meetLanguageInfo.trainingVideoLanguage,
253
- path: trainingVideoPath,
254
311
  localFileName: trainingVideoFileName,
255
- remoteFileName,
256
- url: exports.firebaseUrl + encodeURIComponent(trainingVideoPath) + `?alt=media`,
312
+ remoteFileName: trainingVideoFileName,
313
+ path: trainingVideoPath,
314
+ url: (0, utils_1.firebasePath)(trainingVideoPath),
257
315
  };
258
316
  }
259
317
  let youtubeLink;
@@ -271,33 +329,41 @@ function getLessonInfo({ lessonId, meetLanguageInfo, setInfo, t, useSpokenQuesti
271
329
  video,
272
330
  trainingVideo,
273
331
  youtubeLink,
274
- passagesString: (0, scripturePassages_1.getPassagesString)(lesson.s, meetLanguageInfo),
332
+ passagesString,
275
333
  };
276
334
  }
277
335
  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);
278
343
  return {
279
344
  type: 'dbs',
280
345
  ...baseInfo,
281
346
  fellowshipDuration,
282
347
  applicationDuration,
283
- introLength,
348
+ passagesString,
284
349
  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`,
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),
293
362
  },
294
363
  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`,
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`)),
299
366
  },
300
- passagesString: (0, scripturePassages_1.getPassagesString)(lesson.s, meetLanguageInfo),
301
367
  };
302
368
  }
303
369
  }
@@ -348,18 +414,20 @@ function shouldShowLesson(lessonInfo, languageInfo) {
348
414
  return false;
349
415
  }
350
416
  else if (lessonInfo.type === 'eq' &&
351
- !languageAssets_1.languageAssets[languageInfo.introBridge ?? languageInfo.languageId].find((f) => f === lessonInfo.eq.remoteFileName)) {
417
+ mediaDurations_1.mediaDurations[languageInfo.contentLanguages.intros]?.[lessonInfo.eq.remoteFileName] === undefined) {
352
418
  console.log('Missing remote eq file for lesson:', lessonInfo.lessonId);
353
419
  return false;
354
420
  }
355
421
  else if (lessonInfo.type === 'dbs' &&
356
- !languageAssets_1.languageAssets[lessonInfo.meetLanguageId].find((f) => f === lessonInfo.full.remoteFileName)) {
422
+ mediaDurations_1.mediaDurations[lessonInfo.languageId]?.[lessonInfo.full.remoteFileName] ===
423
+ undefined) {
357
424
  console.log('Missing remote dbs file for lesson:', lessonInfo.lessonId);
358
425
  return false;
359
426
  }
360
427
  else if (lessonInfo.type === 'video' &&
361
428
  lessonInfo.video &&
362
- !languageAssets_1.languageAssets[lessonInfo.meetLanguageId].find((f) => f === lessonInfo.video.remoteFileName)) {
429
+ mediaDurations_1.mediaDurations[lessonInfo.languageId]?.[lessonInfo.video.remoteFileName] ===
430
+ undefined) {
363
431
  console.log('Missing remote video for lesson:', lessonInfo.video.remoteFileName);
364
432
  return false;
365
433
  }
@@ -367,101 +435,21 @@ function shouldShowLesson(lessonInfo, languageInfo) {
367
435
  return true;
368
436
  }
369
437
  /**
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.
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.
373
443
  */
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)
444
+ function getExpectedLessonDuration({ enrichedSections, lessonInfo, }) {
445
+ let computedLength = 0;
446
+ for (const section of enrichedSections) {
447
+ if (!section.chapter)
451
448
  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
- }
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;
459
453
  }
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 };
454
+ return computedLength;
467
455
  }
@@ -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),