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.
- package/dist/data/areas/areas.json +48 -50
- package/dist/data/areas/areas.schema.json +2 -2
- package/dist/data/areas/areas.zod.d.ts +1 -1
- package/dist/data/areas/areas.zod.js +1 -1
- package/dist/data/areas/index.d.ts +1 -1
- package/dist/data/bibleAudios/bibleAudios.json +314 -581
- package/dist/data/bibleAudios/bibleAudios.schema.json +8 -17
- package/dist/data/bibleAudios/bibleAudios.zod.d.ts +2 -43
- package/dist/data/bibleAudios/bibleAudios.zod.js +7 -16
- package/dist/data/bibleAudios/index.d.ts +1 -2
- package/dist/data/bibleBooks/bibleBooks.json +782 -784
- package/dist/data/bibleStatuses/bibleStatuses.json +8284 -7785
- package/dist/data/bibleStatuses/bibleStatuses.schema.json +0 -6
- package/dist/data/bibleStatuses/bibleStatuses.zod.d.ts +0 -1
- package/dist/data/bibleStatuses/bibleStatuses.zod.js +0 -4
- package/dist/data/bibleStatuses/index.d.ts +0 -1
- package/dist/data/bibleTexts/bibleTexts.json +184 -269
- package/dist/data/bibleTexts/bibleTexts.schema.json +0 -7
- package/dist/data/bibleTexts/bibleTexts.zod.d.ts +0 -28
- package/dist/data/bibleTexts/bibleTexts.zod.js +3 -10
- package/dist/data/bibleTexts/index.d.ts +0 -2
- package/dist/data/clones/clones.json +0 -2
- package/dist/data/countries/countries.json +480 -480
- package/dist/data/countriesAndLanguages/countriesAndLanguages.json +6798 -6800
- package/dist/data/crowdinLanguages/crowdinLanguages.json +1438 -1440
- package/dist/data/crowdinLanguages/crowdinLanguages.schema.json +3 -3
- package/dist/data/crowdinLanguages/crowdinLanguages.zod.d.ts +2 -2
- package/dist/data/crowdinLanguages/crowdinLanguages.zod.js +2 -2
- package/dist/data/crowdinLanguages/index.d.ts +2 -2
- package/dist/data/curriculumFoundations/curriculumFoundations.json +69 -86
- package/dist/data/curriculumQuestions/curriculumQuestions.json +192 -194
- package/dist/data/curriculumTopics/curriculumTopics.json +8 -51
- package/dist/data/dblAudioLicenses/dblAudioLicenses.json +7 -36
- package/dist/data/dblTextLicenses/dblTextLicenses.json +4 -4
- package/dist/data/iosVoiceOverLanguages/iosVoiceOverLanguages.json +1 -1
- package/dist/data/iso6933LanguageCodes/iso6933LanguageCodes.json +1 -1
- package/dist/data/languageAssets/index.d.ts +1 -0
- package/dist/data/languageAssets/index.js +7 -0
- package/dist/data/languageAssets/languageAssets.json +45406 -0
- package/dist/data/languageAssets/languageAssets.schema.json +19 -0
- package/dist/data/languageAssets/languageAssets.zod.d.ts +3 -0
- package/dist/data/languageAssets/languageAssets.zod.js +7 -0
- package/dist/data/languages/index.d.ts +10 -20
- package/dist/data/languages/languages.json +2008 -2983
- package/dist/data/languages/languages.schema.json +47 -93
- package/dist/data/languages/languages.zod.d.ts +30 -93
- package/dist/data/languages/languages.zod.js +37 -59
- package/dist/data/lessonPauses/index.d.ts +1 -0
- package/dist/data/lessonPauses/lessonPauses.json +3 -2
- package/dist/data/lessonPauses/lessonPauses.schema.json +11 -1
- package/dist/data/lessonPauses/lessonPauses.zod.d.ts +1 -0
- package/dist/data/lessonPauses/lessonPauses.zod.js +3 -0
- package/dist/data/mediaDurations/mediaDurations.json +4231 -33446
- package/dist/data/mediaDurations/mediaDurations.schema.json +1 -1
- package/dist/data/mediaDurations/mediaDurations.zod.js +1 -1
- package/dist/data/notification/index.d.ts +1 -1
- package/dist/data/notification/notification.json +169 -93
- package/dist/data/phoneLanguages/phoneLanguages.json +635 -637
- package/dist/data/questions/questions.json +147 -149
- package/dist/data/releaseNotes/releaseNotes.json +124 -126
- package/dist/data/screenshots/screenshots.json +1 -1
- package/dist/data/scripts/scripts.json +1 -6
- package/dist/data/sets/index.d.ts +3 -3
- package/dist/data/sets/sets.json +476 -1766
- package/dist/data/sets/sets.schema.json +1 -1
- package/dist/data/sets/sets.zod.d.ts +3 -3
- package/dist/data/sets/sets.zod.js +6 -3
- package/dist/data/translationsApp/index.d.ts +0 -1
- package/dist/data/translationsApp/translationsApp.json +1582 -1623
- package/dist/data/translationsApp/translationsApp.schema.json +0 -1
- package/dist/data/translationsApp/translationsApp.zod.d.ts +0 -2
- package/dist/data/translationsApp/translationsApp.zod.js +0 -1
- package/dist/data/translationsFtb/translationsFtb.json +613 -69
- package/dist/data/translationsIntroduction/translationsIntroduction.json +1 -1
- package/dist/data/translationsQuestion/translationsQuestion.json +33 -65
- package/dist/data/translationsSet/translationsSet.json +19565 -30302
- package/dist/data/translationsSet/translationsSet.schema.json +4 -3
- package/dist/data/translationsSet/translationsSet.zod.js +4 -4
- package/dist/data/translationsSolarSpeaker/translationsSolarSpeaker.json +1 -1
- package/dist/data/translationsSpokenQuestion/translationsSpokenQuestion.json +33 -65
- package/dist/data/youtubePlaylists/youtubePlaylists.json +10 -10
- package/dist/data/youtubeVideos/youtubeVideos.json +82 -82
- package/dist/functions/languages.d.ts +7 -6
- package/dist/functions/languages.js +53 -51
- package/dist/functions/scripturePassages.d.ts +49 -34
- package/dist/functions/scripturePassages.js +304 -167
- package/dist/functions/sets.d.ts +52 -13
- package/dist/functions/sets.js +205 -193
- package/dist/types/bibleChapters.d.ts +2 -2
- package/dist/types/bibleChapters.js +1 -1
- package/dist/types/languages.d.ts +9 -39
- package/dist/types/languages.js +0 -2
- package/dist/types/sets.d.ts +11 -21
- package/package.json +1 -1
- package/dist/data/firebase.d.ts +0 -1
- package/dist/data/firebase.js +0 -4
- package/dist/functions/bibleBooks.d.ts +0 -16
- package/dist/functions/bibleBooks.js +0 -101
- package/dist/functions/bibles.d.ts +0 -44
- package/dist/functions/bibles.js +0 -291
- package/dist/functions/utils.d.ts +0 -2
- package/dist/functions/utils.js +0 -8
|
@@ -1,33 +1,61 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseVerseRange =
|
|
3
|
+
exports.parseVerseRange = parseVerseRange;
|
|
4
4
|
exports.getChapterUrl = getChapterUrl;
|
|
5
5
|
exports.getPassagesString = getPassagesString;
|
|
6
6
|
exports.getScripturePassage = getScripturePassage;
|
|
7
7
|
exports.getLessonScripture = getLessonScripture;
|
|
8
|
+
exports.parseBibleSnapshot = parseBibleSnapshot;
|
|
8
9
|
exports.verseToSuperscript = verseToSuperscript;
|
|
9
|
-
exports.
|
|
10
|
-
exports.enrichSections = enrichSections;
|
|
10
|
+
exports.getLessonPauses = getLessonPauses;
|
|
11
11
|
exports.normalizeVerseTimings = normalizeVerseTimings;
|
|
12
12
|
const bibleStatuses_1 = require("../data/bibleStatuses");
|
|
13
|
-
const
|
|
13
|
+
const lessonPauses_1 = require("../data/lessonPauses");
|
|
14
14
|
const mediaDurations_1 = require("../data/mediaDurations");
|
|
15
|
+
const specialIds_1 = require("../data/specialIds");
|
|
15
16
|
const languages_1 = require("../functions/languages");
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
const bibleChapters_1 = require("../types/bibleChapters");
|
|
18
|
+
const sets_1 = require("./sets");
|
|
19
|
+
/**
|
|
20
|
+
* Parses a verse range string like "GEN.1.1-GEN.1.25" or "JHN.3.16" or "REV.22"
|
|
21
|
+
* Returns the start and end verse IDs
|
|
22
|
+
*/
|
|
23
|
+
function parseVerseRange(passageId) {
|
|
24
|
+
// Handle single verse: "GEN.1.1" -> "GEN.1.1-GEN.1.1"
|
|
25
|
+
const parts = passageId.includes('-')
|
|
26
|
+
? passageId.split('-')
|
|
27
|
+
: [passageId, passageId];
|
|
28
|
+
const startParts = parts[0].split('.');
|
|
29
|
+
const endParts = parts[1].split('.');
|
|
30
|
+
const startBook = startParts[0];
|
|
31
|
+
const startChapter = startParts[1];
|
|
32
|
+
const startVerse = startParts.length > 2 ? startParts[2] : '1';
|
|
33
|
+
const endBook = endParts[0];
|
|
34
|
+
const endChapter = endParts[1];
|
|
35
|
+
const endVerse = endParts.length > 2 ? endParts[2] : '1';
|
|
36
|
+
return {
|
|
37
|
+
startBook,
|
|
38
|
+
startChapter,
|
|
39
|
+
startVerse,
|
|
40
|
+
endBook,
|
|
41
|
+
endChapter,
|
|
42
|
+
endVerse,
|
|
43
|
+
startChapterId: `${startBook}.${startChapter}`,
|
|
44
|
+
endChapterId: `${endBook}.${endChapter}`,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
21
47
|
function getChapterUrl(params) {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
48
|
+
const startBook = 'startBook' in params
|
|
49
|
+
? params.startBook
|
|
50
|
+
: parseVerseRange(params.passageId).startBook;
|
|
51
|
+
const startChapter = 'startChapter' in params
|
|
52
|
+
? params.startChapter
|
|
53
|
+
: parseVerseRange(params.passageId).startChapter;
|
|
54
|
+
return (sets_1.firebaseUrl +
|
|
27
55
|
encodeURIComponent('audio_bibles' +
|
|
28
56
|
`/${params.bibleAudioId}` +
|
|
29
|
-
`/${
|
|
30
|
-
`/${
|
|
57
|
+
`/${startBook}` +
|
|
58
|
+
`/${startBook}_${startChapter.padStart(3, '0')}.mp3`) +
|
|
31
59
|
`?alt=media`);
|
|
32
60
|
}
|
|
33
61
|
/** Extracts verses from a chapter based on verse numbers */
|
|
@@ -57,90 +85,170 @@ function extractVersesFromChapter(chapter, startVerse, endVerse) {
|
|
|
57
85
|
* ["GEN.1.1-GEN.1.25", "GEN.2.4-GEN.2.7", "EXO.3.3-EXO.3.25"] -> "Genesis 1: 1-25, 2: 4-7, Exodus 3: 3-25"
|
|
58
86
|
* ["JHN.1.1"] -> "John 1: 1"
|
|
59
87
|
*/
|
|
60
|
-
function getPassagesString(
|
|
88
|
+
function getPassagesString(passageIds, language) {
|
|
89
|
+
if (!passageIds || passageIds.length === 0)
|
|
90
|
+
return '';
|
|
61
91
|
const parts = [];
|
|
62
92
|
let currentBook = null;
|
|
63
|
-
for (const
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
93
|
+
for (const passageId of passageIds) {
|
|
94
|
+
const { startBook, startChapter, startVerse, endChapter, endVerse } = parseVerseRange(passageId);
|
|
95
|
+
let bookName =
|
|
96
|
+
// American sign language should use English book names.
|
|
97
|
+
language.languageId === 'ase'
|
|
98
|
+
? bibleStatuses_1.bibleStatuses.NLT.bookNames[startBook]
|
|
99
|
+
: language.bible.bookNames[startBook];
|
|
100
|
+
if (!bookName) {
|
|
101
|
+
bookName = language.bibleFallback?.bookNames[startBook];
|
|
102
|
+
if (!bookName)
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// New book - include book name
|
|
106
|
+
if (currentBook !== startBook) {
|
|
107
|
+
currentBook = startBook;
|
|
108
|
+
// Same chapter
|
|
109
|
+
if (startChapter === endChapter) {
|
|
110
|
+
if (startVerse === endVerse) {
|
|
111
|
+
parts.push(`${bookName} ${startChapter}: ${startVerse}`);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
parts.push(`${bookName} ${startChapter}: ${startVerse}-${endVerse}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Different chapters
|
|
119
|
+
parts.push(`${bookName} ${startChapter}: ${startVerse}-${endChapter}: ${endVerse}`);
|
|
120
|
+
}
|
|
75
121
|
}
|
|
76
122
|
else {
|
|
77
|
-
|
|
123
|
+
// Same book - omit book name
|
|
124
|
+
if (startChapter === endChapter) {
|
|
125
|
+
if (startVerse === endVerse) {
|
|
126
|
+
parts.push(`${startChapter}: ${startVerse}`);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
parts.push(`${startChapter}: ${startVerse}-${endVerse}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
// Different chapters
|
|
134
|
+
parts.push(`${startChapter}: ${startVerse}-${endChapter}: ${endVerse}`);
|
|
135
|
+
}
|
|
78
136
|
}
|
|
79
137
|
}
|
|
80
|
-
return parts.join(', ');
|
|
138
|
+
return (0, languages_1.numerals)(parts.join(', '), language.script.name);
|
|
81
139
|
}
|
|
82
140
|
/** Gets a scripture passage by extracting verses from chapters documents. */
|
|
83
|
-
async function getScripturePassage({ bibleId, passageId, getChapter,
|
|
84
|
-
const bibleInfo = (0, bibles_1.getBibleInfo)(bibleId);
|
|
85
|
-
const { startVerse, endVerse, chapterId } = (0, bibleBooks_1.parseVerseRange)(passageId);
|
|
86
|
-
const noTextAvailableReturn = {
|
|
87
|
-
bibleId,
|
|
88
|
-
header: getPassagesString([{ passageId, bibleId }]),
|
|
89
|
-
passageId,
|
|
90
|
-
verses: [],
|
|
91
|
-
};
|
|
92
|
-
if (!bibleInfo)
|
|
93
|
-
return noTextAvailableReturn;
|
|
94
|
-
const returnExpected = bibleInfo.availableTextChapters.includes(chapterId) ||
|
|
95
|
-
bibleInfo.availableTimingsChapters.includes(chapterId);
|
|
96
|
-
/**
|
|
97
|
-
* In this case, we know the bible does not have text for this chapter, so the
|
|
98
|
-
* user will see a placeholder verse.
|
|
99
|
-
*/
|
|
100
|
-
if (!returnExpected) {
|
|
101
|
-
noTextAvailableReturn.verses = [
|
|
102
|
-
{ text: noTextAvailableString, verseId: '' },
|
|
103
|
-
];
|
|
104
|
-
return noTextAvailableReturn;
|
|
105
|
-
}
|
|
141
|
+
async function getScripturePassage({ bibleId, bibleFallbackId, passageId, getChapter, languageInfo, }) {
|
|
106
142
|
try {
|
|
143
|
+
const { startBook, startChapter, startVerse, endBook, endChapter, endVerse, } = parseVerseRange(passageId);
|
|
107
144
|
const startVerseNum = parseInt(startVerse);
|
|
108
145
|
const endVerseNum = parseInt(endVerse);
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
146
|
+
// Same chapter - simple case
|
|
147
|
+
if (startBook === endBook && startChapter === endChapter) {
|
|
148
|
+
const chapterId = `${startBook}.${startChapter}`;
|
|
149
|
+
let chapter = await getChapter({ chapterId, bibleId });
|
|
150
|
+
if (!chapter && bibleFallbackId)
|
|
151
|
+
chapter = await getChapter({ chapterId, bibleId: bibleFallbackId });
|
|
152
|
+
if (!chapter) {
|
|
153
|
+
console.warn(`Chapter not found: ${bibleId}.${chapterId} for passage ${passageId}`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const verses = extractVersesFromChapter(chapter, startVerseNum, endVerseNum);
|
|
157
|
+
if (verses.length === 0) {
|
|
158
|
+
console.warn(`No verses found for passage ${passageId}`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
passageId,
|
|
163
|
+
bibleId,
|
|
164
|
+
header: getPassagesString([passageId], languageInfo),
|
|
165
|
+
verses,
|
|
166
|
+
};
|
|
116
167
|
}
|
|
117
|
-
|
|
118
|
-
|
|
168
|
+
// Multi-chapter passage (rare but possible)
|
|
169
|
+
// Example: GEN.1.26-GEN.2.3
|
|
170
|
+
const chapters = [];
|
|
171
|
+
// Get all chapters in the range
|
|
172
|
+
const startChapterNum = parseInt(startChapter);
|
|
173
|
+
const endChapterNum = parseInt(endChapter);
|
|
174
|
+
for (let i = startChapterNum; i <= endChapterNum; i++) {
|
|
175
|
+
const chapterId = `${startBook}.${i}`;
|
|
176
|
+
let chapter = await getChapter({ chapterId, bibleId });
|
|
177
|
+
if (!chapter && bibleFallbackId)
|
|
178
|
+
chapter = await getChapter({ chapterId, bibleId: bibleFallbackId });
|
|
179
|
+
if (chapter) {
|
|
180
|
+
chapters.push(chapter);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
console.warn(`Missing chapter in range: ${bibleId}.${chapterId}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (chapters.length === 0) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
// Extract verses from each chapter
|
|
190
|
+
const allVerses = [];
|
|
191
|
+
chapters.forEach((chapter, index) => {
|
|
192
|
+
if (index === 0) {
|
|
193
|
+
// First chapter - from startVerse to end
|
|
194
|
+
const lastVerse = Math.max(...chapter.verses.map((v) => parseInt(v.verseId.split('.')[2])));
|
|
195
|
+
allVerses.push(...extractVersesFromChapter(chapter, startVerseNum, lastVerse));
|
|
196
|
+
}
|
|
197
|
+
else if (index === chapters.length - 1) {
|
|
198
|
+
// Last chapter - from beginning to endVerse
|
|
199
|
+
allVerses.push(...extractVersesFromChapter(chapter, 1, endVerseNum));
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// Middle chapters - all verses
|
|
203
|
+
allVerses.push(...chapter.verses);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
119
206
|
return {
|
|
120
207
|
passageId,
|
|
121
|
-
bibleId
|
|
122
|
-
header: getPassagesString([
|
|
123
|
-
verses,
|
|
208
|
+
bibleId,
|
|
209
|
+
header: getPassagesString([passageId], languageInfo),
|
|
210
|
+
verses: allVerses,
|
|
124
211
|
};
|
|
125
212
|
}
|
|
126
213
|
catch (error) {
|
|
127
214
|
console.error(`Error getting scripture passage ${passageId} in ${bibleId}`, error);
|
|
128
|
-
|
|
215
|
+
return;
|
|
129
216
|
}
|
|
130
217
|
}
|
|
131
|
-
/**
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
218
|
+
/**
|
|
219
|
+
* Gets multiple scripture passages efficiently (Future optimization: could
|
|
220
|
+
* batch the chapter fetches)
|
|
221
|
+
*/
|
|
222
|
+
async function getLessonScripture(params) {
|
|
223
|
+
const passages = await Promise.all(params.passageIds.map((passageId) => getScripturePassage({ ...params, passageId })));
|
|
224
|
+
return passages;
|
|
225
|
+
}
|
|
226
|
+
function parseBibleSnapshot(snapshot, bible) {
|
|
227
|
+
const validChapters = [];
|
|
228
|
+
if (!snapshot) {
|
|
229
|
+
console.warn('No snapshot for', bible);
|
|
230
|
+
return [];
|
|
231
|
+
}
|
|
232
|
+
else if (snapshot.empty) {
|
|
233
|
+
console.warn('Empty snapshot for', bible);
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
236
|
+
for (const doc of snapshot.docs) {
|
|
237
|
+
const chapterParse = bibleChapters_1.BibleChapter.safeParse(doc.data());
|
|
238
|
+
if (!chapterParse.success) {
|
|
239
|
+
console.warn('Skipping invalid bible chapter document', doc.id, chapterParse.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`));
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
validChapters.push(chapterParse.data);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (validChapters.length === 0) {
|
|
246
|
+
console.warn('No valid chapters for', bible);
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
return validChapters;
|
|
251
|
+
}
|
|
144
252
|
}
|
|
145
253
|
/**
|
|
146
254
|
* Converts a verse number to a superscript string. This is useful for
|
|
@@ -177,102 +285,131 @@ function verseToSuperscript(num) {
|
|
|
177
285
|
.join('');
|
|
178
286
|
}
|
|
179
287
|
/**
|
|
180
|
-
*
|
|
181
|
-
*
|
|
288
|
+
* Splits a passage's verses into per-chapter chunks. Single-chapter passages
|
|
289
|
+
* produce one chunk. Multi-chapter passages (rare, e.g. GEN.1.26-GEN.2.3)
|
|
290
|
+
* produce one chunk per chapter, with isPartOfPrevious=true on the 2nd+ chunks.
|
|
291
|
+
* This mirrors leviathan's passage_ids_by_chapter splitting logic.
|
|
182
292
|
*/
|
|
183
|
-
function
|
|
184
|
-
if (
|
|
185
|
-
return
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const storySections = lessonInfo.sections.filter((s) => s.chapter === sets_1.Chapter.STORY);
|
|
199
|
-
// Forward pass: compute lengths and start times for all non-application
|
|
200
|
-
// sections.
|
|
201
|
-
let currentTime = 0;
|
|
202
|
-
const enriched = lessonInfo.sections.map((section) => {
|
|
203
|
-
if (!section.chapter)
|
|
204
|
-
return { ...section, startTime: 0, length: 0 };
|
|
205
|
-
currentTime += section.pauseBefore ?? 0;
|
|
206
|
-
let length;
|
|
207
|
-
const startTime = currentTime;
|
|
208
|
-
if (section.chapter !== sets_1.Chapter.STORY) {
|
|
209
|
-
length = mediaDurations_1.mediaDurations[section.languageId]?.[section.fileName];
|
|
293
|
+
function splitPassageIntoChunks(verses) {
|
|
294
|
+
if (verses.length === 0)
|
|
295
|
+
return [];
|
|
296
|
+
const chunks = [];
|
|
297
|
+
let currentChapterId = null;
|
|
298
|
+
for (const verse of verses) {
|
|
299
|
+
const parts = verse.verseId.split('.');
|
|
300
|
+
const chapterId = `${parts[0]}.${parts[1]}`;
|
|
301
|
+
if (chapterId !== currentChapterId) {
|
|
302
|
+
chunks.push({
|
|
303
|
+
verses: [verse],
|
|
304
|
+
bookId: parts[0],
|
|
305
|
+
isPartOfPrevious: currentChapterId !== null,
|
|
306
|
+
});
|
|
307
|
+
currentChapterId = chapterId;
|
|
210
308
|
}
|
|
211
309
|
else {
|
|
212
|
-
|
|
213
|
-
const [bibleId, passageId] = section.id.split(';');
|
|
214
|
-
length = bibleStatuses_1.bibleStatuses[bibleId]?.customPassages?.[passageId];
|
|
215
|
-
if (!length) {
|
|
216
|
-
const passage = scripture[storySections.indexOf(section)];
|
|
217
|
-
if (passage) {
|
|
218
|
-
const firstTimed = passage.verses.find((v) => v.timings);
|
|
219
|
-
const lastTimed = [...passage.verses].reverse().find((v) => v.timings);
|
|
220
|
-
if (firstTimed?.timings && lastTimed?.timings) {
|
|
221
|
-
length = lastTimed.timings[1] - firstTimed.timings[0];
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
if (length == null) {
|
|
227
|
-
console.warn(`Missing duration for section ${section.id} (${section.chapter}, ${section.fileName})`);
|
|
228
|
-
}
|
|
229
|
-
currentTime += length ?? 0;
|
|
230
|
-
return { ...section, length: length ?? 0, startTime };
|
|
231
|
-
});
|
|
232
|
-
// Backward pass: compute application section start times from the end of the
|
|
233
|
-
// lesson file, so they are always correct even if story durations are
|
|
234
|
-
// slightly off.
|
|
235
|
-
const fullFileName = lessonInfo.type === 'dbs'
|
|
236
|
-
? lessonInfo.full.localFileName
|
|
237
|
-
: lessonInfo.video.localFileName;
|
|
238
|
-
const totalDuration = mediaDurations_1.mediaDurations[lessonInfo.languageId]?.[fullFileName] ?? 0;
|
|
239
|
-
if (totalDuration > 0) {
|
|
240
|
-
let timeFromEnd = totalDuration;
|
|
241
|
-
const applicationSections = enriched.filter((s) => s.chapter === sets_1.Chapter.APPLICATION);
|
|
242
|
-
for (const section of [...applicationSections].reverse()) {
|
|
243
|
-
timeFromEnd -= section.length;
|
|
244
|
-
section.startTime = timeFromEnd;
|
|
245
|
-
timeFromEnd -= section.pauseBefore ?? 0;
|
|
310
|
+
chunks[chunks.length - 1].verses.push(verse);
|
|
246
311
|
}
|
|
247
312
|
}
|
|
248
|
-
return
|
|
313
|
+
return chunks;
|
|
249
314
|
}
|
|
250
315
|
/**
|
|
251
|
-
*
|
|
252
|
-
*
|
|
316
|
+
* Some lessons were built using a system that used slightly different pause
|
|
317
|
+
* values.
|
|
253
318
|
*/
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
319
|
+
const alternativeLessonPauses = {
|
|
320
|
+
beforeStory: 1,
|
|
321
|
+
afterStory: 1,
|
|
322
|
+
betweenPassages: 0.5,
|
|
323
|
+
beforeFtb: 1,
|
|
324
|
+
afterFtb: 0.5,
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* List of languages whose lessons were built using slightly different pause
|
|
328
|
+
* values.
|
|
329
|
+
*/
|
|
330
|
+
const usesAlternativeLessonPauses = ['tel', 'som', 'kmr', 'swz', 'vie'];
|
|
331
|
+
/** Returns the correct lesson pauses for a given language. */
|
|
332
|
+
function getLessonPauses(languageId) {
|
|
333
|
+
return usesAlternativeLessonPauses.includes(languageId)
|
|
334
|
+
? alternativeLessonPauses
|
|
335
|
+
: lessonPauses_1.lessonPauses;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Converts verse timings within each scripture passage from chapter-relative to
|
|
339
|
+
* full-lesson relative. This includes accounting for question durations,
|
|
340
|
+
* pauses, and from-the-book durations.
|
|
341
|
+
*
|
|
342
|
+
* Multi-chapter passages are split into per-chapter chunks internally, since
|
|
343
|
+
* the audio is clipped from individual chapter files.
|
|
344
|
+
*/
|
|
345
|
+
function normalizeVerseTimings({ lessonInfo, scripture, }) {
|
|
346
|
+
const languageInfo = (0, languages_1.getLanguageInfo)(lessonInfo.meetLanguageId);
|
|
347
|
+
const pauses = getLessonPauses(lessonInfo.meetLanguageId);
|
|
348
|
+
let cumulativeTime = lessonInfo.fellowshipDuration + pauses.beforeStory;
|
|
349
|
+
if (specialIds_1.specialIds.introductionLessonIds.includes(lessonInfo.lessonId) &&
|
|
350
|
+
lessonInfo.type === 'dbs' &&
|
|
351
|
+
lessonInfo.introLength)
|
|
352
|
+
cumulativeTime += lessonInfo.introLength;
|
|
353
|
+
let lastBook = null;
|
|
354
|
+
/** Tracks the overall passage index (not chunk index) for FTB/pause logic. */
|
|
355
|
+
let globalChunkIndex = 0;
|
|
356
|
+
return scripture?.map((passage) => {
|
|
357
|
+
if (!passage)
|
|
358
|
+
return undefined;
|
|
359
|
+
const chunks = splitPassageIntoChunks(passage.verses);
|
|
360
|
+
if (chunks.length === 0)
|
|
262
361
|
return passage;
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
firstTimedVerse.timings
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
362
|
+
const adjustedVerses = [];
|
|
363
|
+
for (const chunk of chunks) {
|
|
364
|
+
const firstTimedVerse = chunk.verses.find((v) => v.timings);
|
|
365
|
+
if (!firstTimedVerse?.timings) {
|
|
366
|
+
// No timing data — pass through unadjusted
|
|
367
|
+
adjustedVerses.push(...chunk.verses);
|
|
368
|
+
if (!chunk.isPartOfPrevious)
|
|
369
|
+
globalChunkIndex++;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const chunkAudioStart = firstTimedVerse.timings[0];
|
|
373
|
+
// Add FTB + pauses. Chunks that are continuations of a multi-chapter
|
|
374
|
+
// passage (isPartOfPrevious) get no pause — they are concatenated directly.
|
|
375
|
+
if (!chunk.isPartOfPrevious) {
|
|
376
|
+
if (lastBook !== chunk.bookId) {
|
|
377
|
+
if (globalChunkIndex !== 0) {
|
|
378
|
+
cumulativeTime += pauses.beforeFtb;
|
|
379
|
+
}
|
|
380
|
+
const ftbLanguage = languageInfo.titles ?? languageInfo.languageId;
|
|
381
|
+
const ftbKey = `${ftbLanguage}.${chunk.bookId}.mp3`;
|
|
382
|
+
const ftbDuration = mediaDurations_1.mediaDurations[ftbLanguage]?.[ftbKey];
|
|
383
|
+
if (ftbDuration != null)
|
|
384
|
+
cumulativeTime += ftbDuration;
|
|
385
|
+
else {
|
|
386
|
+
console.error(`FTB duration not found for ${ftbKey}`);
|
|
387
|
+
return passage;
|
|
388
|
+
}
|
|
389
|
+
cumulativeTime += pauses.afterFtb;
|
|
390
|
+
lastBook = chunk.bookId;
|
|
391
|
+
}
|
|
392
|
+
else if (globalChunkIndex !== 0) {
|
|
393
|
+
cumulativeTime += pauses.betweenPassages;
|
|
394
|
+
}
|
|
395
|
+
globalChunkIndex++;
|
|
396
|
+
}
|
|
397
|
+
const offset = cumulativeTime - chunkAudioStart;
|
|
398
|
+
for (const verse of chunk.verses) {
|
|
269
399
|
if (!verse.timings)
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
400
|
+
adjustedVerses.push(verse);
|
|
401
|
+
else
|
|
402
|
+
adjustedVerses.push({
|
|
403
|
+
...verse,
|
|
404
|
+
timings: [verse.timings[0] + offset, verse.timings[1] + offset],
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
// Advance cumulative time by this chunk's audio duration
|
|
408
|
+
const lastTimedVerse = [...chunk.verses].reverse().find((v) => v.timings);
|
|
409
|
+
if (lastTimedVerse?.timings) {
|
|
410
|
+
cumulativeTime += lastTimedVerse.timings[1] - chunkAudioStart;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return { ...passage, verses: adjustedVerses };
|
|
277
414
|
});
|
|
278
415
|
}
|
package/dist/functions/sets.d.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { TranslationsApp } from '../data/translationsApp/translationsApp.zod';
|
|
2
2
|
import type { LanguageInfo, MeetTranslations } from '../types/languages';
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import { ScripturePassage } from '../types/scripturePassages';
|
|
4
|
+
import { DbsInfo, Lesson, LessonInfo, SetInfo, VideoInfo } from '../types/sets';
|
|
5
|
+
export declare const firebaseUrl = "https://firebasestorage.googleapis.com/v0/b/waha-app-db.appspot.com/o/";
|
|
6
|
+
export declare function getSetInfo({ setId, meetLanguageId, setIds, t, }: {
|
|
5
7
|
setId: string;
|
|
6
|
-
|
|
8
|
+
meetLanguageId: string;
|
|
7
9
|
t: MeetTranslations & TranslationsApp[string];
|
|
8
10
|
setIds: string[];
|
|
9
11
|
}): SetInfo | undefined;
|
|
10
|
-
export declare function getLessonInfo({ lessonId,
|
|
12
|
+
export declare function getLessonInfo({ lessonId, meetLanguageInfo, setInfo, t, useSpokenQuestions, }: {
|
|
11
13
|
lessonId: Lesson['lessonId'] | undefined;
|
|
12
|
-
|
|
14
|
+
meetLanguageInfo: LanguageInfo;
|
|
13
15
|
setInfo?: SetInfo;
|
|
14
16
|
t: MeetTranslations & TranslationsApp[string];
|
|
15
17
|
useSpokenQuestions?: boolean;
|
|
@@ -18,13 +20,50 @@ export declare function convertSToString(time: number): string;
|
|
|
18
20
|
/** Determines whether a lesson should be visible in Waha. */
|
|
19
21
|
export declare function shouldShowLesson(lessonInfo: LessonInfo | undefined, languageInfo: LanguageInfo): boolean;
|
|
20
22
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
* Calculate start times for each section based on question durations and total
|
|
24
|
+
* audio duration. Must be called after audio is loaded to get accurate total
|
|
25
|
+
* duration.
|
|
26
|
+
*/
|
|
27
|
+
export declare function calculateSectionTimings({ languageInfo, thisLesson, totalDuration, normalizedScripture, supportsVerseHighlight, }: {
|
|
28
|
+
thisLesson: DbsInfo | VideoInfo;
|
|
29
|
+
totalDuration: number;
|
|
30
|
+
languageInfo: LanguageInfo;
|
|
31
|
+
normalizedScripture: Array<ScripturePassage | undefined>;
|
|
32
|
+
/**
|
|
33
|
+
* Determine if the timings are valid to be used for section timings.
|
|
34
|
+
*
|
|
35
|
+
* Supports verse highlight example:
|
|
36
|
+
*
|
|
37
|
+
* - GEN.1.1-GEN.1.10 -> 83
|
|
38
|
+
* - GEN.2.1-GEN.2.4 -> 100
|
|
39
|
+
* - GEN.2.7-GEN.2.10 -> 120
|
|
40
|
+
*
|
|
41
|
+
* Doesn't support verse highlight example:
|
|
42
|
+
*
|
|
43
|
+
* - GEN.1.1-GEN.1.10 -> 83
|
|
44
|
+
* - GEN.2.1-GEN.2.4 -> undefined
|
|
45
|
+
* - GEN.2.7-GEN.2.10 -> undefined
|
|
46
|
+
*
|
|
47
|
+
* First passage is always valid because its start time is simply the length
|
|
48
|
+
* of the previous audio. Subsequent passages require good verse timing data.
|
|
49
|
+
*/
|
|
50
|
+
supportsVerseHighlight: boolean;
|
|
51
|
+
}): Map<string, number>;
|
|
52
|
+
/**
|
|
53
|
+
* Checks if pre-normalized verse timings for given scripture are valid. Does
|
|
54
|
+
* this by comparing the real duration of the full lesson mp3 file with the
|
|
55
|
+
* calculated duration based on individual question durations and verse timing
|
|
56
|
+
* data from bible chapter documents. For lessons generated programmatically
|
|
57
|
+
* with recent systems, they should be nearly identical. For older lessons that
|
|
58
|
+
* were made by hand, they may be off. If they're too off, they will be flagged
|
|
59
|
+
* as invalid and the lesson will default to not using verse timings.
|
|
26
60
|
*/
|
|
27
|
-
export declare function
|
|
28
|
-
enrichedSections: EnrichedSection[];
|
|
61
|
+
export declare function getSupportsVerseHighlight({ lessonInfo, scripture, totalDuration, }: {
|
|
29
62
|
lessonInfo: DbsInfo | VideoInfo;
|
|
30
|
-
|
|
63
|
+
scripture: Array<ScripturePassage | undefined>;
|
|
64
|
+
totalDuration: number | undefined;
|
|
65
|
+
}): {
|
|
66
|
+
computedLength: number;
|
|
67
|
+
diff: number;
|
|
68
|
+
passed: boolean;
|
|
69
|
+
};
|