@churchapps/content-provider-helper 0.0.3 → 0.0.4
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/index.cjs +446 -361
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -27
- package/dist/index.d.ts +73 -27
- package/dist/index.js +439 -359
- package/dist/index.js.map +1 -1
- package/package.json +55 -55
package/dist/index.js
CHANGED
|
@@ -18,11 +18,11 @@ function detectMediaType(url, explicitType) {
|
|
|
18
18
|
const videoPatterns = [".mp4", ".webm", ".m3u8", ".mov", "stream.mux.com"];
|
|
19
19
|
return videoPatterns.some((p) => url.includes(p)) ? "video" : "image";
|
|
20
20
|
}
|
|
21
|
-
function createFolder(id, title, path, image,
|
|
22
|
-
return { type: "folder", id, title, path, image, isLeaf
|
|
21
|
+
function createFolder(id, title, path, image, isLeaf) {
|
|
22
|
+
return { type: "folder", id, title, path, image, isLeaf };
|
|
23
23
|
}
|
|
24
24
|
function createFile(id, title, url, options) {
|
|
25
|
-
return { type: "file", id, title, url, mediaType: options?.mediaType ?? detectMediaType(url), image: options?.image, muxPlaybackId: options?.muxPlaybackId,
|
|
25
|
+
return { type: "file", id, title, url, mediaType: options?.mediaType ?? detectMediaType(url), image: options?.image, muxPlaybackId: options?.muxPlaybackId, seconds: options?.seconds, loop: options?.loop, loopVideo: options?.loopVideo, streamUrl: options?.streamUrl };
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// src/pathUtils.ts
|
|
@@ -49,10 +49,61 @@ function appendToPath(basePath, segment) {
|
|
|
49
49
|
return cleanBase + "/" + segment;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// src/instructionPathUtils.ts
|
|
53
|
+
function navigateToPath(instructions, path) {
|
|
54
|
+
if (!path || !instructions?.items) return null;
|
|
55
|
+
const indices = path.split(".").map(Number);
|
|
56
|
+
if (indices.some(isNaN)) return null;
|
|
57
|
+
let current = instructions.items[indices[0]] || null;
|
|
58
|
+
for (let i = 1; i < indices.length && current; i++) {
|
|
59
|
+
current = current.children?.[indices[i]] || null;
|
|
60
|
+
}
|
|
61
|
+
return current;
|
|
62
|
+
}
|
|
63
|
+
function generatePath(indices) {
|
|
64
|
+
return indices.join(".");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/durationUtils.ts
|
|
68
|
+
var DEFAULT_DURATION_CONFIG = {
|
|
69
|
+
secondsPerImage: 15,
|
|
70
|
+
wordsPerMinute: 150
|
|
71
|
+
};
|
|
72
|
+
function countWords(text) {
|
|
73
|
+
if (!text || !text.trim()) return 0;
|
|
74
|
+
return text.trim().split(/\s+/).length;
|
|
75
|
+
}
|
|
76
|
+
function estimateImageDuration(config = {}) {
|
|
77
|
+
return config.secondsPerImage ?? DEFAULT_DURATION_CONFIG.secondsPerImage;
|
|
78
|
+
}
|
|
79
|
+
function estimateTextDuration(text, config = {}) {
|
|
80
|
+
const words = countWords(text);
|
|
81
|
+
const wpm = config.wordsPerMinute ?? DEFAULT_DURATION_CONFIG.wordsPerMinute;
|
|
82
|
+
return Math.ceil(words / wpm * 60);
|
|
83
|
+
}
|
|
84
|
+
function estimateDuration(mediaType, options) {
|
|
85
|
+
const config = options?.config ?? {};
|
|
86
|
+
switch (mediaType) {
|
|
87
|
+
case "image":
|
|
88
|
+
return estimateImageDuration(config);
|
|
89
|
+
case "text":
|
|
90
|
+
if (options?.wordCount) {
|
|
91
|
+
const wpm = config.wordsPerMinute ?? DEFAULT_DURATION_CONFIG.wordsPerMinute;
|
|
92
|
+
return Math.ceil(options.wordCount / wpm * 60);
|
|
93
|
+
}
|
|
94
|
+
if (options?.text) {
|
|
95
|
+
return estimateTextDuration(options.text, config);
|
|
96
|
+
}
|
|
97
|
+
return 0;
|
|
98
|
+
case "video":
|
|
99
|
+
default:
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
52
104
|
// src/FormatConverters.ts
|
|
53
105
|
var FormatConverters_exports = {};
|
|
54
106
|
__export(FormatConverters_exports, {
|
|
55
|
-
collapseInstructions: () => collapseInstructions,
|
|
56
107
|
expandedInstructionsToPlaylist: () => expandedInstructionsToPlaylist,
|
|
57
108
|
expandedInstructionsToPresentations: () => expandedInstructionsToPresentations,
|
|
58
109
|
instructionsToPlaylist: () => instructionsToPlaylist,
|
|
@@ -61,7 +112,6 @@ __export(FormatConverters_exports, {
|
|
|
61
112
|
playlistToInstructions: () => playlistToInstructions,
|
|
62
113
|
playlistToPresentations: () => playlistToPresentations,
|
|
63
114
|
presentationsToExpandedInstructions: () => presentationsToExpandedInstructions,
|
|
64
|
-
presentationsToInstructions: () => presentationsToInstructions,
|
|
65
115
|
presentationsToPlaylist: () => presentationsToPlaylist
|
|
66
116
|
});
|
|
67
117
|
function generateId() {
|
|
@@ -105,21 +155,15 @@ function presentationsToPlaylist(plan) {
|
|
|
105
155
|
}
|
|
106
156
|
return files;
|
|
107
157
|
}
|
|
108
|
-
function presentationsToInstructions(plan) {
|
|
109
|
-
return { venueName: plan.name, items: plan.sections.map((section) => ({ id: section.id, itemType: "section", label: section.name, children: section.presentations.map((pres) => {
|
|
110
|
-
const totalSeconds = pres.files.reduce((sum, f) => sum + (f.providerData?.seconds || 0), 0);
|
|
111
|
-
return { id: pres.id, itemType: mapActionTypeToItemType(pres.actionType), label: pres.name, seconds: totalSeconds || void 0, embedUrl: pres.files[0]?.embedUrl || pres.files[0]?.url };
|
|
112
|
-
}) })) };
|
|
113
|
-
}
|
|
114
158
|
function presentationsToExpandedInstructions(plan) {
|
|
115
|
-
return { venueName: plan.name, items: plan.sections.map((section) => ({ id: section.id, itemType: "section", label: section.name, children: section.presentations.map((pres) => ({ id: pres.id, itemType: mapActionTypeToItemType(pres.actionType), label: pres.name, description: pres.actionType !== "other" ? pres.actionType : void 0, seconds: pres.files.reduce((sum, f) => sum + (f.
|
|
159
|
+
return { venueName: plan.name, items: plan.sections.map((section) => ({ id: section.id, itemType: "section", label: section.name, children: section.presentations.map((pres) => ({ id: pres.id, itemType: mapActionTypeToItemType(pres.actionType), label: pres.name, description: pres.actionType !== "other" ? pres.actionType : void 0, seconds: pres.files.reduce((sum, f) => sum + (f.seconds || 0), 0) || void 0, children: pres.files.map((f) => ({ id: f.id, itemType: "file", label: f.title, seconds: f.seconds, embedUrl: f.embedUrl || f.url })) })) })) };
|
|
116
160
|
}
|
|
117
161
|
function instructionsToPlaylist(instructions) {
|
|
118
162
|
const files = [];
|
|
119
163
|
function extractFiles(items) {
|
|
120
164
|
for (const item of items) {
|
|
121
165
|
if (item.embedUrl && (item.itemType === "file" || !item.children?.length)) {
|
|
122
|
-
files.push({ type: "file", id: item.id || item.relatedId || generateId(), title: item.label || "Untitled", mediaType: detectMediaType(item.embedUrl), url: item.embedUrl, embedUrl: item.embedUrl,
|
|
166
|
+
files.push({ type: "file", id: item.id || item.relatedId || generateId(), title: item.label || "Untitled", mediaType: detectMediaType(item.embedUrl), url: item.embedUrl, embedUrl: item.embedUrl, seconds: item.seconds });
|
|
123
167
|
}
|
|
124
168
|
if (item.children) {
|
|
125
169
|
extractFiles(item.children);
|
|
@@ -138,14 +182,14 @@ function instructionsToPresentations(instructions, planId) {
|
|
|
138
182
|
if (presItem.children && presItem.children.length > 0) {
|
|
139
183
|
for (const child of presItem.children) {
|
|
140
184
|
if (child.embedUrl) {
|
|
141
|
-
const file = { type: "file", id: child.id || child.relatedId || generateId(), title: child.label || "Untitled", mediaType: detectMediaType(child.embedUrl), url: child.embedUrl, embedUrl: child.embedUrl,
|
|
185
|
+
const file = { type: "file", id: child.id || child.relatedId || generateId(), title: child.label || "Untitled", mediaType: detectMediaType(child.embedUrl), url: child.embedUrl, embedUrl: child.embedUrl, seconds: child.seconds };
|
|
142
186
|
allFiles.push(file);
|
|
143
187
|
files.push(file);
|
|
144
188
|
}
|
|
145
189
|
}
|
|
146
190
|
}
|
|
147
191
|
if (files.length === 0 && presItem.embedUrl) {
|
|
148
|
-
const file = { type: "file", id: presItem.id || presItem.relatedId || generateId(), title: presItem.label || "Untitled", mediaType: detectMediaType(presItem.embedUrl), url: presItem.embedUrl, embedUrl: presItem.embedUrl,
|
|
192
|
+
const file = { type: "file", id: presItem.id || presItem.relatedId || generateId(), title: presItem.label || "Untitled", mediaType: detectMediaType(presItem.embedUrl), url: presItem.embedUrl, embedUrl: presItem.embedUrl, seconds: presItem.seconds };
|
|
149
193
|
allFiles.push(file);
|
|
150
194
|
files.push(file);
|
|
151
195
|
}
|
|
@@ -156,40 +200,12 @@ function instructionsToPresentations(instructions, planId) {
|
|
|
156
200
|
return { id: planId || generateId(), name: instructions.venueName || "Plan", sections, allFiles };
|
|
157
201
|
}
|
|
158
202
|
var expandedInstructionsToPresentations = instructionsToPresentations;
|
|
159
|
-
function collapseInstructions(instructions, maxDepth = 2) {
|
|
160
|
-
function collapseItem(item, currentDepth) {
|
|
161
|
-
if (currentDepth >= maxDepth || !item.children || item.children.length === 0) {
|
|
162
|
-
const { children, ...rest } = item;
|
|
163
|
-
if (children && children.length > 0) {
|
|
164
|
-
const totalSeconds = children.reduce((sum, c) => sum + (c.seconds || 0), 0);
|
|
165
|
-
if (totalSeconds > 0) {
|
|
166
|
-
rest.seconds = totalSeconds;
|
|
167
|
-
}
|
|
168
|
-
if (!rest.embedUrl) {
|
|
169
|
-
const firstWithUrl = children.find((c) => c.embedUrl);
|
|
170
|
-
if (firstWithUrl) {
|
|
171
|
-
rest.embedUrl = firstWithUrl.embedUrl;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return rest;
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
...item,
|
|
179
|
-
children: item.children.map((child) => collapseItem(child, currentDepth + 1))
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
return {
|
|
183
|
-
venueName: instructions.venueName,
|
|
184
|
-
items: instructions.items.map((item) => collapseItem(item, 0))
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
203
|
function playlistToPresentations(files, planName = "Playlist", sectionName = "Content") {
|
|
188
204
|
const presentations = files.map((file, index) => ({ id: `pres-${index}-${file.id}`, name: file.title, actionType: "play", files: [file] }));
|
|
189
205
|
return { id: "playlist-plan-" + generateId(), name: planName, sections: [{ id: "main-section", name: sectionName, presentations }], allFiles: [...files] };
|
|
190
206
|
}
|
|
191
207
|
function playlistToInstructions(files, venueName = "Playlist") {
|
|
192
|
-
return { venueName, items: [{ id: "main-section", itemType: "section", label: "Content", children: files.map((file, index) => ({ id: file.id || `item-${index}`, itemType: "file", label: file.title, seconds: file.
|
|
208
|
+
return { venueName, items: [{ id: "main-section", itemType: "section", label: "Content", children: files.map((file, index) => ({ id: file.id || `item-${index}`, itemType: "file", label: file.title, seconds: file.seconds, embedUrl: file.embedUrl || file.url })) }] };
|
|
193
209
|
}
|
|
194
210
|
var playlistToExpandedInstructions = playlistToInstructions;
|
|
195
211
|
|
|
@@ -217,14 +233,10 @@ var FormatResolver = class {
|
|
|
217
233
|
const plan = await this.provider.getPresentations(path, auth);
|
|
218
234
|
if (plan) return presentationsToPlaylist(plan);
|
|
219
235
|
}
|
|
220
|
-
if (caps.
|
|
221
|
-
const expanded = await this.provider.
|
|
236
|
+
if (caps.instructions && this.provider.getInstructions) {
|
|
237
|
+
const expanded = await this.provider.getInstructions(path, auth);
|
|
222
238
|
if (expanded) return instructionsToPlaylist(expanded);
|
|
223
239
|
}
|
|
224
|
-
if (this.options.allowLossy && caps.instructions && this.provider.getInstructions) {
|
|
225
|
-
const instructions = await this.provider.getInstructions(path, auth);
|
|
226
|
-
if (instructions) return instructionsToPlaylist(instructions);
|
|
227
|
-
}
|
|
228
240
|
return null;
|
|
229
241
|
}
|
|
230
242
|
async getPlaylistWithMeta(path, auth) {
|
|
@@ -239,13 +251,9 @@ var FormatResolver = class {
|
|
|
239
251
|
const plan = await this.provider.getPresentations(path, auth);
|
|
240
252
|
if (plan) return { data: presentationsToPlaylist(plan), meta: { isNative: false, sourceFormat: "presentations", isLossy: false } };
|
|
241
253
|
}
|
|
242
|
-
if (caps.
|
|
243
|
-
const expanded = await this.provider.
|
|
244
|
-
if (expanded) return { data: instructionsToPlaylist(expanded), meta: { isNative: false, sourceFormat: "
|
|
245
|
-
}
|
|
246
|
-
if (this.options.allowLossy && caps.instructions && this.provider.getInstructions) {
|
|
247
|
-
const instructions = await this.provider.getInstructions(path, auth);
|
|
248
|
-
if (instructions) return { data: instructionsToPlaylist(instructions), meta: { isNative: false, sourceFormat: "instructions", isLossy: true } };
|
|
254
|
+
if (caps.instructions && this.provider.getInstructions) {
|
|
255
|
+
const expanded = await this.provider.getInstructions(path, auth);
|
|
256
|
+
if (expanded) return { data: instructionsToPlaylist(expanded), meta: { isNative: false, sourceFormat: "instructions", isLossy: false } };
|
|
249
257
|
}
|
|
250
258
|
return { data: null, meta: { isNative: false, isLossy: false } };
|
|
251
259
|
}
|
|
@@ -256,13 +264,9 @@ var FormatResolver = class {
|
|
|
256
264
|
const result = await this.provider.getPresentations(path, auth);
|
|
257
265
|
if (result) return result;
|
|
258
266
|
}
|
|
259
|
-
if (caps.expandedInstructions && this.provider.getExpandedInstructions) {
|
|
260
|
-
const expanded = await this.provider.getExpandedInstructions(path, auth);
|
|
261
|
-
if (expanded) return instructionsToPresentations(expanded, fallbackId);
|
|
262
|
-
}
|
|
263
267
|
if (caps.instructions && this.provider.getInstructions) {
|
|
264
|
-
const
|
|
265
|
-
if (
|
|
268
|
+
const expanded = await this.provider.getInstructions(path, auth);
|
|
269
|
+
if (expanded) return instructionsToPresentations(expanded, fallbackId);
|
|
266
270
|
}
|
|
267
271
|
if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
268
272
|
const playlist = await this.provider.getPlaylist(path, auth);
|
|
@@ -281,13 +285,9 @@ var FormatResolver = class {
|
|
|
281
285
|
return { data: result, meta: { isNative: true, isLossy: false } };
|
|
282
286
|
}
|
|
283
287
|
}
|
|
284
|
-
if (caps.expandedInstructions && this.provider.getExpandedInstructions) {
|
|
285
|
-
const expanded = await this.provider.getExpandedInstructions(path, auth);
|
|
286
|
-
if (expanded) return { data: instructionsToPresentations(expanded, fallbackId), meta: { isNative: false, sourceFormat: "expandedInstructions", isLossy: false } };
|
|
287
|
-
}
|
|
288
288
|
if (caps.instructions && this.provider.getInstructions) {
|
|
289
|
-
const
|
|
290
|
-
if (
|
|
289
|
+
const expanded = await this.provider.getInstructions(path, auth);
|
|
290
|
+
if (expanded) return { data: instructionsToPresentations(expanded, fallbackId), meta: { isNative: false, sourceFormat: "instructions", isLossy: false } };
|
|
291
291
|
}
|
|
292
292
|
if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
293
293
|
const playlist = await this.provider.getPlaylist(path, auth);
|
|
@@ -302,13 +302,9 @@ var FormatResolver = class {
|
|
|
302
302
|
const result = await this.provider.getInstructions(path, auth);
|
|
303
303
|
if (result) return result;
|
|
304
304
|
}
|
|
305
|
-
if (caps.expandedInstructions && this.provider.getExpandedInstructions) {
|
|
306
|
-
const expanded = await this.provider.getExpandedInstructions(path, auth);
|
|
307
|
-
if (expanded) return collapseInstructions(expanded);
|
|
308
|
-
}
|
|
309
305
|
if (caps.presentations) {
|
|
310
306
|
const plan = await this.provider.getPresentations(path, auth);
|
|
311
|
-
if (plan) return
|
|
307
|
+
if (plan) return presentationsToExpandedInstructions(plan);
|
|
312
308
|
}
|
|
313
309
|
if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
314
310
|
const playlist = await this.provider.getPlaylist(path, auth);
|
|
@@ -327,60 +323,10 @@ var FormatResolver = class {
|
|
|
327
323
|
return { data: result, meta: { isNative: true, isLossy: false } };
|
|
328
324
|
}
|
|
329
325
|
}
|
|
330
|
-
if (caps.expandedInstructions && this.provider.getExpandedInstructions) {
|
|
331
|
-
const expanded = await this.provider.getExpandedInstructions(path, auth);
|
|
332
|
-
if (expanded) return { data: collapseInstructions(expanded), meta: { isNative: false, sourceFormat: "expandedInstructions", isLossy: true } };
|
|
333
|
-
}
|
|
334
|
-
if (caps.presentations) {
|
|
335
|
-
const plan = await this.provider.getPresentations(path, auth);
|
|
336
|
-
if (plan) return { data: presentationsToInstructions(plan), meta: { isNative: false, sourceFormat: "presentations", isLossy: false } };
|
|
337
|
-
}
|
|
338
|
-
if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
339
|
-
const playlist = await this.provider.getPlaylist(path, auth);
|
|
340
|
-
if (playlist && playlist.length > 0) return { data: playlistToInstructions(playlist, fallbackTitle), meta: { isNative: false, sourceFormat: "playlist", isLossy: true } };
|
|
341
|
-
}
|
|
342
|
-
return { data: null, meta: { isNative: false, isLossy: false } };
|
|
343
|
-
}
|
|
344
|
-
async getExpandedInstructions(path, auth) {
|
|
345
|
-
const caps = this.provider.capabilities;
|
|
346
|
-
const fallbackTitle = this.getIdFromPath(path);
|
|
347
|
-
if (caps.expandedInstructions && this.provider.getExpandedInstructions) {
|
|
348
|
-
const result = await this.provider.getExpandedInstructions(path, auth);
|
|
349
|
-
if (result) return result;
|
|
350
|
-
}
|
|
351
|
-
if (caps.presentations) {
|
|
352
|
-
const plan = await this.provider.getPresentations(path, auth);
|
|
353
|
-
if (plan) return presentationsToExpandedInstructions(plan);
|
|
354
|
-
}
|
|
355
|
-
if (caps.instructions && this.provider.getInstructions) {
|
|
356
|
-
const instructions = await this.provider.getInstructions(path, auth);
|
|
357
|
-
if (instructions) return instructions;
|
|
358
|
-
}
|
|
359
|
-
if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
360
|
-
const playlist = await this.provider.getPlaylist(path, auth);
|
|
361
|
-
if (playlist && playlist.length > 0) {
|
|
362
|
-
return playlistToInstructions(playlist, fallbackTitle);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
return null;
|
|
366
|
-
}
|
|
367
|
-
async getExpandedInstructionsWithMeta(path, auth) {
|
|
368
|
-
const caps = this.provider.capabilities;
|
|
369
|
-
const fallbackTitle = this.getIdFromPath(path);
|
|
370
|
-
if (caps.expandedInstructions && this.provider.getExpandedInstructions) {
|
|
371
|
-
const result = await this.provider.getExpandedInstructions(path, auth);
|
|
372
|
-
if (result) {
|
|
373
|
-
return { data: result, meta: { isNative: true, isLossy: false } };
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
326
|
if (caps.presentations) {
|
|
377
327
|
const plan = await this.provider.getPresentations(path, auth);
|
|
378
328
|
if (plan) return { data: presentationsToExpandedInstructions(plan), meta: { isNative: false, sourceFormat: "presentations", isLossy: false } };
|
|
379
329
|
}
|
|
380
|
-
if (caps.instructions && this.provider.getInstructions) {
|
|
381
|
-
const instructions = await this.provider.getInstructions(path, auth);
|
|
382
|
-
if (instructions) return { data: instructions, meta: { isNative: false, sourceFormat: "instructions", isLossy: true } };
|
|
383
|
-
}
|
|
384
330
|
if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
385
331
|
const playlist = await this.provider.getPlaylist(path, auth);
|
|
386
332
|
if (playlist && playlist.length > 0) return { data: playlistToInstructions(playlist, fallbackTitle), meta: { isNative: false, sourceFormat: "playlist", isLossy: true } };
|
|
@@ -437,22 +383,13 @@ var OAuthHelper = class {
|
|
|
437
383
|
code_verifier: codeVerifier
|
|
438
384
|
});
|
|
439
385
|
const tokenUrl = `${config.oauthBase}/token`;
|
|
440
|
-
console.log(`${providerId} token exchange request to: ${tokenUrl}`);
|
|
441
|
-
console.log(` - client_id: ${config.clientId}`);
|
|
442
|
-
console.log(` - redirect_uri: ${redirectUri}`);
|
|
443
|
-
console.log(` - code: ${code.substring(0, 10)}...`);
|
|
444
386
|
const response = await fetch(tokenUrl, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: params.toString() });
|
|
445
|
-
console.log(`${providerId} token response status: ${response.status}`);
|
|
446
387
|
if (!response.ok) {
|
|
447
|
-
const errorText = await response.text();
|
|
448
|
-
console.error(`${providerId} token exchange failed: ${response.status} - ${errorText}`);
|
|
449
388
|
return null;
|
|
450
389
|
}
|
|
451
390
|
const data = await response.json();
|
|
452
|
-
console.log(`${providerId} token exchange successful, got access_token: ${!!data.access_token}`);
|
|
453
391
|
return { access_token: data.access_token, refresh_token: data.refresh_token, token_type: data.token_type || "Bearer", created_at: Math.floor(Date.now() / 1e3), expires_in: data.expires_in, scope: data.scope || config.scopes.join(" ") };
|
|
454
|
-
} catch
|
|
455
|
-
console.error(`${providerId} token exchange error:`, error);
|
|
392
|
+
} catch {
|
|
456
393
|
return null;
|
|
457
394
|
}
|
|
458
395
|
}
|
|
@@ -537,25 +474,19 @@ var ApiHelper = class {
|
|
|
537
474
|
if (!auth) return null;
|
|
538
475
|
return { Authorization: `Bearer ${auth.access_token}`, Accept: "application/json" };
|
|
539
476
|
}
|
|
540
|
-
async apiRequest(config,
|
|
477
|
+
async apiRequest(config, _providerId, path, auth, method = "GET", body) {
|
|
541
478
|
try {
|
|
542
479
|
const url = `${config.apiBase}${path}`;
|
|
543
480
|
const headers = { Accept: "application/json" };
|
|
544
481
|
if (auth) headers["Authorization"] = `Bearer ${auth.access_token}`;
|
|
545
482
|
if (body) headers["Content-Type"] = "application/json";
|
|
546
|
-
console.log(`${providerId} API request: ${method} ${url}`);
|
|
547
|
-
console.log(`${providerId} API auth present: ${!!auth}`);
|
|
548
483
|
const options = { method, headers, ...body ? { body: JSON.stringify(body) } : {} };
|
|
549
484
|
const response = await fetch(url, options);
|
|
550
|
-
console.log(`${providerId} API response status: ${response.status}`);
|
|
551
485
|
if (!response.ok) {
|
|
552
|
-
const errorText = await response.text();
|
|
553
|
-
console.error(`${providerId} API request failed: ${response.status} - ${errorText}`);
|
|
554
486
|
return null;
|
|
555
487
|
}
|
|
556
488
|
return await response.json();
|
|
557
|
-
} catch
|
|
558
|
-
console.error(`${providerId} API request error:`, error);
|
|
489
|
+
} catch {
|
|
559
490
|
return null;
|
|
560
491
|
}
|
|
561
492
|
}
|
|
@@ -578,18 +509,6 @@ var ContentProvider = class {
|
|
|
578
509
|
return null;
|
|
579
510
|
}
|
|
580
511
|
async getInstructions(path, auth) {
|
|
581
|
-
const caps = this.getCapabilities();
|
|
582
|
-
if (caps.expandedInstructions) {
|
|
583
|
-
const expanded = await this.getExpandedInstructions(path, auth);
|
|
584
|
-
if (expanded) return collapseInstructions(expanded);
|
|
585
|
-
}
|
|
586
|
-
if (caps.presentations) {
|
|
587
|
-
const plan = await this.getPresentations(path, auth);
|
|
588
|
-
if (plan) return presentationsToInstructions(plan);
|
|
589
|
-
}
|
|
590
|
-
return null;
|
|
591
|
-
}
|
|
592
|
-
async getExpandedInstructions(path, auth) {
|
|
593
512
|
const caps = this.getCapabilities();
|
|
594
513
|
if (caps.presentations) {
|
|
595
514
|
const plan = await this.getPresentations(path, auth);
|
|
@@ -601,7 +520,7 @@ var ContentProvider = class {
|
|
|
601
520
|
return !!this.config.clientId;
|
|
602
521
|
}
|
|
603
522
|
getCapabilities() {
|
|
604
|
-
return { browse: true, presentations: false, playlist: false, instructions: false,
|
|
523
|
+
return { browse: true, presentations: false, playlist: false, instructions: false, mediaLicensing: false };
|
|
605
524
|
}
|
|
606
525
|
checkMediaLicense(_mediaId, _auth) {
|
|
607
526
|
return Promise.resolve(null);
|
|
@@ -656,11 +575,11 @@ var ContentProvider = class {
|
|
|
656
575
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth, method, body);
|
|
657
576
|
}
|
|
658
577
|
// Content factories
|
|
659
|
-
createFolder(id, title, path, image,
|
|
660
|
-
return { type: "folder", id, title, path, image, isLeaf
|
|
578
|
+
createFolder(id, title, path, image, isLeaf) {
|
|
579
|
+
return { type: "folder", id, title, path, image, isLeaf };
|
|
661
580
|
}
|
|
662
581
|
createFile(id, title, url, options) {
|
|
663
|
-
return { type: "file", id, title, url, mediaType: options?.mediaType ?? detectMediaType(url), image: options?.image, muxPlaybackId: options?.muxPlaybackId,
|
|
582
|
+
return { type: "file", id, title, url, mediaType: options?.mediaType ?? detectMediaType(url), image: options?.image, muxPlaybackId: options?.muxPlaybackId, seconds: options?.seconds, loop: options?.loop, loopVideo: options?.loopVideo, streamUrl: options?.streamUrl };
|
|
664
583
|
}
|
|
665
584
|
};
|
|
666
585
|
|
|
@@ -674,14 +593,13 @@ var APlayProvider = class {
|
|
|
674
593
|
this.config = { id: "aplay", name: "APlay", apiBase: "https://api-prod.amazingkids.app", oauthBase: "https://api.joinamazing.com/prod/aims/oauth", clientId: "xFJFq7yNYuXXXMx0YBiQ", scopes: ["openid", "profile", "email"], endpoints: { modules: "/prod/curriculum/modules", productLibraries: (productId) => `/prod/curriculum/modules/products/${productId}/libraries`, libraryMedia: (libraryId) => `/prod/creators/libraries/${libraryId}/media` } };
|
|
675
594
|
this.requiresAuth = true;
|
|
676
595
|
this.authTypes = ["oauth_pkce"];
|
|
677
|
-
this.capabilities = { browse: true, presentations: true, playlist:
|
|
596
|
+
this.capabilities = { browse: true, presentations: true, playlist: true, instructions: true, mediaLicensing: true };
|
|
678
597
|
}
|
|
679
598
|
async apiRequest(path, auth) {
|
|
680
599
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
681
600
|
}
|
|
682
601
|
async browse(path, auth) {
|
|
683
602
|
const { segments, depth } = parsePath(path);
|
|
684
|
-
console.log("APlay browse called with path:", path, "depth:", depth);
|
|
685
603
|
if (depth === 0) {
|
|
686
604
|
return [{
|
|
687
605
|
type: "folder",
|
|
@@ -714,12 +632,9 @@ var APlayProvider = class {
|
|
|
714
632
|
return [];
|
|
715
633
|
}
|
|
716
634
|
async getModules(auth) {
|
|
717
|
-
console.log(`APlay fetching modules from: ${this.config.endpoints.modules}`);
|
|
718
635
|
const response = await this.apiRequest(this.config.endpoints.modules, auth);
|
|
719
|
-
console.log("APlay modules response:", response ? "received" : "null");
|
|
720
636
|
if (!response) return [];
|
|
721
637
|
const modules = response.data || response.modules || response;
|
|
722
|
-
console.log("APlay modules count:", Array.isArray(modules) ? modules.length : "not an array");
|
|
723
638
|
if (!Array.isArray(modules)) return [];
|
|
724
639
|
const items = [];
|
|
725
640
|
for (const m of modules) {
|
|
@@ -727,77 +642,46 @@ var APlayProvider = class {
|
|
|
727
642
|
const moduleId = m.id || m.moduleId;
|
|
728
643
|
const moduleTitle = m.title || m.name;
|
|
729
644
|
const moduleImage = m.image;
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
image: moduleImage,
|
|
738
|
-
path: `/modules/${moduleId}`,
|
|
739
|
-
providerData: { productCount: 0 }
|
|
740
|
-
});
|
|
741
|
-
} else if (products.length === 1) {
|
|
742
|
-
const product = products[0];
|
|
743
|
-
items.push({
|
|
744
|
-
type: "folder",
|
|
745
|
-
id: product.productId || product.id,
|
|
746
|
-
title: moduleTitle,
|
|
747
|
-
image: moduleImage || product.image,
|
|
748
|
-
path: `/modules/${moduleId}`,
|
|
749
|
-
providerData: { productCount: 1, productId: product.productId || product.id }
|
|
750
|
-
});
|
|
751
|
-
} else {
|
|
752
|
-
items.push({
|
|
753
|
-
type: "folder",
|
|
754
|
-
id: moduleId,
|
|
755
|
-
title: moduleTitle,
|
|
756
|
-
image: moduleImage,
|
|
757
|
-
path: `/modules/${moduleId}`,
|
|
758
|
-
providerData: {
|
|
759
|
-
productCount: products.length,
|
|
760
|
-
products: products.map((p) => ({
|
|
761
|
-
id: p.productId || p.id,
|
|
762
|
-
title: p.title || p.name,
|
|
763
|
-
image: p.image
|
|
764
|
-
}))
|
|
765
|
-
}
|
|
766
|
-
});
|
|
767
|
-
}
|
|
645
|
+
items.push({
|
|
646
|
+
type: "folder",
|
|
647
|
+
id: moduleId,
|
|
648
|
+
title: moduleTitle,
|
|
649
|
+
image: moduleImage,
|
|
650
|
+
path: `/modules/${moduleId}`
|
|
651
|
+
});
|
|
768
652
|
}
|
|
769
653
|
return items;
|
|
770
654
|
}
|
|
771
655
|
async getModuleContent(moduleId, currentPath, auth) {
|
|
772
|
-
const
|
|
773
|
-
|
|
656
|
+
const response = await this.apiRequest(this.config.endpoints.modules, auth);
|
|
657
|
+
if (!response) return [];
|
|
658
|
+
const modules = response.data || response.modules || response;
|
|
659
|
+
if (!Array.isArray(modules)) return [];
|
|
660
|
+
const module = modules.find((m) => (m.id || m.moduleId) === moduleId);
|
|
774
661
|
if (!module) return [];
|
|
775
|
-
const
|
|
776
|
-
const
|
|
777
|
-
if (
|
|
778
|
-
|
|
662
|
+
const allProducts = module.products || [];
|
|
663
|
+
const products = allProducts.filter((p) => !p.isHidden);
|
|
664
|
+
if (products.length === 0) {
|
|
665
|
+
return this.getLibraryFolders(moduleId, `${currentPath}/libraries`, auth);
|
|
666
|
+
} else if (products.length === 1) {
|
|
667
|
+
const productId = products[0].productId || products[0].id;
|
|
779
668
|
return this.getLibraryFolders(productId, `${currentPath}/libraries`, auth);
|
|
780
669
|
} else {
|
|
781
|
-
const products = providerData?.products || [];
|
|
782
670
|
return products.map((p) => ({
|
|
783
671
|
type: "folder",
|
|
784
|
-
id: p.id,
|
|
785
|
-
title: p.title,
|
|
672
|
+
id: p.productId || p.id,
|
|
673
|
+
title: p.title || p.name,
|
|
786
674
|
image: p.image,
|
|
787
|
-
path: `${currentPath}/products/${p.id}`
|
|
675
|
+
path: `${currentPath}/products/${p.productId || p.id}`
|
|
788
676
|
}));
|
|
789
677
|
}
|
|
790
678
|
}
|
|
791
679
|
async getLibraryFolders(productId, currentPath, auth) {
|
|
792
|
-
console.log("APlay getLibraryFolders called with productId:", productId);
|
|
793
680
|
const pathFn = this.config.endpoints.productLibraries;
|
|
794
681
|
const apiPath = pathFn(productId);
|
|
795
|
-
console.log(`APlay fetching libraries from: ${apiPath}`);
|
|
796
682
|
const response = await this.apiRequest(apiPath, auth);
|
|
797
|
-
console.log("APlay libraries response:", response ? "received" : "null");
|
|
798
683
|
if (!response) return [];
|
|
799
684
|
const libraries = response.data || response.libraries || response;
|
|
800
|
-
console.log("APlay libraries count:", Array.isArray(libraries) ? libraries.length : "not an array");
|
|
801
685
|
if (!Array.isArray(libraries)) return [];
|
|
802
686
|
return libraries.map((l) => ({
|
|
803
687
|
type: "folder",
|
|
@@ -809,12 +693,9 @@ var APlayProvider = class {
|
|
|
809
693
|
}));
|
|
810
694
|
}
|
|
811
695
|
async getMediaFiles(libraryId, auth) {
|
|
812
|
-
console.log("APlay getMediaFiles called with libraryId:", libraryId);
|
|
813
696
|
const pathFn = this.config.endpoints.libraryMedia;
|
|
814
697
|
const apiPath = pathFn(libraryId);
|
|
815
|
-
console.log(`APlay fetching media from: ${apiPath}`);
|
|
816
698
|
const response = await this.apiRequest(apiPath, auth);
|
|
817
|
-
console.log("APlay media response:", response ? "received" : "null");
|
|
818
699
|
if (!response) return [];
|
|
819
700
|
const mediaItems = response.data || response.media || response;
|
|
820
701
|
if (!Array.isArray(mediaItems)) return [];
|
|
@@ -865,6 +746,49 @@ var APlayProvider = class {
|
|
|
865
746
|
const presentations = files.map((f) => ({ id: f.id, name: f.title, actionType: "play", files: [f] }));
|
|
866
747
|
return { id: libraryId, name: title, sections: [{ id: `section-${libraryId}`, name: title, presentations }], allFiles: files };
|
|
867
748
|
}
|
|
749
|
+
async getPlaylist(path, auth, _resolution) {
|
|
750
|
+
const { segments, depth } = parsePath(path);
|
|
751
|
+
if (depth < 4 || segments[0] !== "modules") return null;
|
|
752
|
+
let libraryId;
|
|
753
|
+
if (segments[2] === "products" && depth === 5) {
|
|
754
|
+
libraryId = segments[4];
|
|
755
|
+
} else if (segments[2] === "libraries" && depth === 4) {
|
|
756
|
+
libraryId = segments[3];
|
|
757
|
+
} else {
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
760
|
+
const files = await this.getMediaFiles(libraryId, auth);
|
|
761
|
+
return files.length > 0 ? files : null;
|
|
762
|
+
}
|
|
763
|
+
async getInstructions(path, auth) {
|
|
764
|
+
const { segments, depth } = parsePath(path);
|
|
765
|
+
if (depth < 4 || segments[0] !== "modules") return null;
|
|
766
|
+
let libraryId;
|
|
767
|
+
if (segments[2] === "products" && depth === 5) {
|
|
768
|
+
libraryId = segments[4];
|
|
769
|
+
} else if (segments[2] === "libraries" && depth === 4) {
|
|
770
|
+
libraryId = segments[3];
|
|
771
|
+
} else {
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
774
|
+
const files = await this.getMediaFiles(libraryId, auth);
|
|
775
|
+
if (files.length === 0) return null;
|
|
776
|
+
const fileItems = files.map((file) => ({
|
|
777
|
+
id: file.id,
|
|
778
|
+
itemType: "file",
|
|
779
|
+
label: file.title,
|
|
780
|
+
embedUrl: file.url
|
|
781
|
+
}));
|
|
782
|
+
return {
|
|
783
|
+
venueName: "Library",
|
|
784
|
+
items: [{
|
|
785
|
+
id: `section-${libraryId}`,
|
|
786
|
+
itemType: "section",
|
|
787
|
+
label: "Content",
|
|
788
|
+
children: fileItems
|
|
789
|
+
}]
|
|
790
|
+
};
|
|
791
|
+
}
|
|
868
792
|
async checkMediaLicense(mediaId, auth) {
|
|
869
793
|
if (!auth) return null;
|
|
870
794
|
try {
|
|
@@ -894,7 +818,7 @@ var SignPresenterProvider = class {
|
|
|
894
818
|
this.config = { id: "signpresenter", name: "SignPresenter", apiBase: "https://api.signpresenter.com", oauthBase: "https://api.signpresenter.com/oauth", clientId: "lessonsscreen-tv", scopes: ["openid", "profile", "content"], supportsDeviceFlow: true, deviceAuthEndpoint: "/device/authorize", endpoints: { playlists: "/content/playlists", messages: (playlistId) => `/content/playlists/${playlistId}/messages` } };
|
|
895
819
|
this.requiresAuth = true;
|
|
896
820
|
this.authTypes = ["oauth_pkce", "device_flow"];
|
|
897
|
-
this.capabilities = { browse: true, presentations: true, playlist:
|
|
821
|
+
this.capabilities = { browse: true, presentations: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
898
822
|
}
|
|
899
823
|
async apiRequest(path, auth) {
|
|
900
824
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
@@ -945,7 +869,7 @@ var SignPresenterProvider = class {
|
|
|
945
869
|
if (!msg.url) continue;
|
|
946
870
|
const url = msg.url;
|
|
947
871
|
const seconds = msg.seconds;
|
|
948
|
-
files.push({ type: "file", id: msg.id, title: msg.name, mediaType: detectMediaType(url, msg.mediaType), image: msg.thumbnail || msg.image, url, embedUrl: url,
|
|
872
|
+
files.push({ type: "file", id: msg.id, title: msg.name, mediaType: detectMediaType(url, msg.mediaType), image: msg.thumbnail || msg.image, url, embedUrl: url, seconds });
|
|
949
873
|
}
|
|
950
874
|
return files;
|
|
951
875
|
}
|
|
@@ -962,6 +886,39 @@ var SignPresenterProvider = class {
|
|
|
962
886
|
const presentations = files.map((f) => ({ id: f.id, name: f.title, actionType: "play", files: [f] }));
|
|
963
887
|
return { id: playlistId, name: title, image, sections: [{ id: `section-${playlistId}`, name: title, presentations }], allFiles: files };
|
|
964
888
|
}
|
|
889
|
+
async getPlaylist(path, auth, _resolution) {
|
|
890
|
+
const { segments, depth } = parsePath(path);
|
|
891
|
+
if (depth < 2 || segments[0] !== "playlists") return null;
|
|
892
|
+
const playlistId = segments[1];
|
|
893
|
+
const files = await this.getMessages(playlistId, auth);
|
|
894
|
+
return files.length > 0 ? files : null;
|
|
895
|
+
}
|
|
896
|
+
async getInstructions(path, auth) {
|
|
897
|
+
const { segments, depth } = parsePath(path);
|
|
898
|
+
if (depth < 2 || segments[0] !== "playlists") return null;
|
|
899
|
+
const playlistId = segments[1];
|
|
900
|
+
const files = await this.getMessages(playlistId, auth);
|
|
901
|
+
if (files.length === 0) return null;
|
|
902
|
+
const playlists = await this.getPlaylists(auth);
|
|
903
|
+
const playlist = playlists.find((p) => p.id === playlistId);
|
|
904
|
+
const title = playlist?.title || "Playlist";
|
|
905
|
+
const fileItems = files.map((file) => ({
|
|
906
|
+
id: file.id,
|
|
907
|
+
itemType: "file",
|
|
908
|
+
label: file.title,
|
|
909
|
+
seconds: file.seconds,
|
|
910
|
+
embedUrl: file.embedUrl || file.url
|
|
911
|
+
}));
|
|
912
|
+
return {
|
|
913
|
+
venueName: title,
|
|
914
|
+
items: [{
|
|
915
|
+
id: `section-${playlistId}`,
|
|
916
|
+
itemType: "section",
|
|
917
|
+
label: title,
|
|
918
|
+
children: fileItems
|
|
919
|
+
}]
|
|
920
|
+
};
|
|
921
|
+
}
|
|
965
922
|
};
|
|
966
923
|
|
|
967
924
|
// src/providers/lessonsChurch/LessonsChurchProvider.ts
|
|
@@ -973,7 +930,7 @@ var LessonsChurchProvider = class {
|
|
|
973
930
|
this.config = { id: "lessonschurch", name: "Lessons.church", apiBase: "https://api.lessons.church", oauthBase: "", clientId: "", scopes: [], endpoints: { programs: "/programs/public", studies: (programId) => `/studies/public/program/${programId}`, lessons: (studyId) => `/lessons/public/study/${studyId}`, venues: (lessonId) => `/venues/public/lesson/${lessonId}`, playlist: (venueId) => `/venues/playlist/${venueId}`, feed: (venueId) => `/venues/public/feed/${venueId}`, addOns: "/addOns/public", addOnDetail: (id) => `/addOns/public/${id}` } };
|
|
974
931
|
this.requiresAuth = false;
|
|
975
932
|
this.authTypes = ["none"];
|
|
976
|
-
this.capabilities = { browse: true, presentations: true, playlist: true, instructions: true,
|
|
933
|
+
this.capabilities = { browse: true, presentations: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
977
934
|
}
|
|
978
935
|
async getPlaylist(path, _auth, resolution) {
|
|
979
936
|
const venueId = getSegment(path, 4);
|
|
@@ -992,7 +949,7 @@ var LessonsChurchProvider = class {
|
|
|
992
949
|
if (!f.url) continue;
|
|
993
950
|
const url = f.url;
|
|
994
951
|
const fileId = f.id || `playlist-${fileIndex++}`;
|
|
995
|
-
files.push({ type: "file", id: fileId, title: f.name || msg.name, mediaType: detectMediaType(url, f.fileType), image: response.lessonImage, url,
|
|
952
|
+
files.push({ type: "file", id: fileId, title: f.name || msg.name, mediaType: detectMediaType(url, f.fileType), image: response.lessonImage, url, seconds: f.seconds, loop: f.loop, loopVideo: f.loopVideo });
|
|
996
953
|
}
|
|
997
954
|
}
|
|
998
955
|
return files;
|
|
@@ -1009,7 +966,6 @@ var LessonsChurchProvider = class {
|
|
|
1009
966
|
}
|
|
1010
967
|
async browse(path, _auth) {
|
|
1011
968
|
const { segments, depth } = parsePath(path);
|
|
1012
|
-
console.log("[LessonsChurchProvider.browse] path:", path, "depth:", depth, "segments:", segments);
|
|
1013
969
|
if (depth === 0) {
|
|
1014
970
|
return [
|
|
1015
971
|
{ type: "folder", id: "lessons-root", title: "Lessons", path: "/lessons" },
|
|
@@ -1066,9 +1022,7 @@ var LessonsChurchProvider = class {
|
|
|
1066
1022
|
id: l.id,
|
|
1067
1023
|
title: l.name || l.title,
|
|
1068
1024
|
image: l.image,
|
|
1069
|
-
path: `${currentPath}/${l.id}
|
|
1070
|
-
providerData: { lessonImage: l.image }
|
|
1071
|
-
// Keep for display on venues
|
|
1025
|
+
path: `${currentPath}/${l.id}`
|
|
1072
1026
|
}));
|
|
1073
1027
|
}
|
|
1074
1028
|
async getVenues(lessonId, currentPath) {
|
|
@@ -1078,7 +1032,7 @@ var LessonsChurchProvider = class {
|
|
|
1078
1032
|
const lessonResponse = await this.apiRequest(`/lessons/public/${lessonId}`);
|
|
1079
1033
|
const lessonImage = lessonResponse?.image;
|
|
1080
1034
|
const venues = Array.isArray(response) ? response : [];
|
|
1081
|
-
|
|
1035
|
+
return venues.map((v) => ({
|
|
1082
1036
|
type: "folder",
|
|
1083
1037
|
id: v.id,
|
|
1084
1038
|
title: v.name,
|
|
@@ -1086,8 +1040,6 @@ var LessonsChurchProvider = class {
|
|
|
1086
1040
|
isLeaf: true,
|
|
1087
1041
|
path: `${currentPath}/${v.id}`
|
|
1088
1042
|
}));
|
|
1089
|
-
console.log("[LessonsChurchProvider.getVenues] returning:", result.map((r) => ({ id: r.id, title: r.title, isLeaf: r.isLeaf })));
|
|
1090
|
-
return result;
|
|
1091
1043
|
}
|
|
1092
1044
|
async getPlaylistFiles(venueId) {
|
|
1093
1045
|
const files = await this.getPlaylist(`/lessons/_/_/_/${venueId}`, null);
|
|
@@ -1146,7 +1098,7 @@ var LessonsChurchProvider = class {
|
|
|
1146
1098
|
} else {
|
|
1147
1099
|
return null;
|
|
1148
1100
|
}
|
|
1149
|
-
return { type: "file", id: addOn.id, title: addOn.name, mediaType, image: addOn.image, url, embedUrl: `https://lessons.church/embed/addon/${addOn.id}`,
|
|
1101
|
+
return { type: "file", id: addOn.id, title: addOn.name, mediaType, image: addOn.image, url, embedUrl: `https://lessons.church/embed/addon/${addOn.id}`, seconds, loopVideo: video?.loopVideo || false };
|
|
1150
1102
|
}
|
|
1151
1103
|
async getPresentations(path, _auth) {
|
|
1152
1104
|
const venueId = getSegment(path, 4);
|
|
@@ -1157,18 +1109,6 @@ var LessonsChurchProvider = class {
|
|
|
1157
1109
|
return this.convertVenueToPlan(venueData);
|
|
1158
1110
|
}
|
|
1159
1111
|
async getInstructions(path, _auth) {
|
|
1160
|
-
const venueId = getSegment(path, 4);
|
|
1161
|
-
if (!venueId) return null;
|
|
1162
|
-
const response = await this.apiRequest(`/venues/public/planItems/${venueId}`);
|
|
1163
|
-
if (!response) return null;
|
|
1164
|
-
const processItem = (item) => {
|
|
1165
|
-
const itemType = this.normalizeItemType(item.itemType);
|
|
1166
|
-
const relatedId = item.relatedId;
|
|
1167
|
-
return { id: item.id, itemType, relatedId, label: item.label, description: item.description, seconds: item.seconds, children: item.children?.map(processItem), embedUrl: this.getEmbedUrl(itemType, relatedId) };
|
|
1168
|
-
};
|
|
1169
|
-
return { venueName: response.venueName, items: (response.items || []).map(processItem) };
|
|
1170
|
-
}
|
|
1171
|
-
async getExpandedInstructions(path, _auth) {
|
|
1172
1112
|
const venueId = getSegment(path, 4);
|
|
1173
1113
|
if (!venueId) return null;
|
|
1174
1114
|
const [planItemsResponse, actionsResponse] = await Promise.all([
|
|
@@ -1182,7 +1122,8 @@ var LessonsChurchProvider = class {
|
|
|
1182
1122
|
if (section.id && section.actions) {
|
|
1183
1123
|
sectionActionsMap.set(section.id, section.actions.map((action) => {
|
|
1184
1124
|
const embedUrl = this.getEmbedUrl("action", action.id);
|
|
1185
|
-
|
|
1125
|
+
const seconds = action.seconds ?? estimateImageDuration();
|
|
1126
|
+
return { id: action.id, itemType: "action", relatedId: action.id, label: action.name, description: action.actionType, seconds, children: [{ id: action.id + "-file", itemType: "file", label: action.name, seconds, embedUrl }] };
|
|
1186
1127
|
}));
|
|
1187
1128
|
}
|
|
1188
1129
|
}
|
|
@@ -1238,7 +1179,7 @@ var LessonsChurchProvider = class {
|
|
|
1238
1179
|
for (const file of action.files || []) {
|
|
1239
1180
|
if (!file.url) continue;
|
|
1240
1181
|
const embedUrl = action.id ? `https://lessons.church/embed/action/${action.id}` : void 0;
|
|
1241
|
-
const contentFile = { type: "file", id: file.id || "", title: file.name || "", mediaType: detectMediaType(file.url, file.fileType), image: venue.lessonImage, url: file.url, embedUrl,
|
|
1182
|
+
const contentFile = { type: "file", id: file.id || "", title: file.name || "", mediaType: detectMediaType(file.url, file.fileType), image: venue.lessonImage, url: file.url, embedUrl, seconds: file.seconds, streamUrl: file.streamUrl };
|
|
1242
1183
|
files.push(contentFile);
|
|
1243
1184
|
allFiles.push(contentFile);
|
|
1244
1185
|
}
|
|
@@ -1285,22 +1226,13 @@ async function exchangeCodeForTokensWithPKCE(config, code, redirectUri, codeVeri
|
|
|
1285
1226
|
try {
|
|
1286
1227
|
const params = { grant_type: "authorization_code", code, client_id: config.clientId, code_verifier: codeVerifier, redirect_uri: redirectUri };
|
|
1287
1228
|
const tokenUrl = `${config.oauthBase}/token`;
|
|
1288
|
-
console.log(`B1Church PKCE token exchange request to: ${tokenUrl}`);
|
|
1289
|
-
console.log(` - client_id: ${config.clientId}`);
|
|
1290
|
-
console.log(` - redirect_uri: ${redirectUri}`);
|
|
1291
|
-
console.log(` - code: ${code.substring(0, 10)}...`);
|
|
1292
1229
|
const response = await fetch(tokenUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(params) });
|
|
1293
|
-
console.log(`B1Church token response status: ${response.status}`);
|
|
1294
1230
|
if (!response.ok) {
|
|
1295
|
-
const errorText = await response.text();
|
|
1296
|
-
console.error(`B1Church token exchange failed: ${response.status} - ${errorText}`);
|
|
1297
1231
|
return null;
|
|
1298
1232
|
}
|
|
1299
1233
|
const data = await response.json();
|
|
1300
|
-
console.log(`B1Church token exchange successful, got access_token: ${!!data.access_token}`);
|
|
1301
1234
|
return { access_token: data.access_token, refresh_token: data.refresh_token, token_type: data.token_type || "Bearer", created_at: Math.floor(Date.now() / 1e3), expires_in: data.expires_in, scope: data.scope || config.scopes.join(" ") };
|
|
1302
|
-
} catch
|
|
1303
|
-
console.error("B1Church token exchange error:", error);
|
|
1235
|
+
} catch {
|
|
1304
1236
|
return null;
|
|
1305
1237
|
}
|
|
1306
1238
|
}
|
|
@@ -1308,22 +1240,13 @@ async function exchangeCodeForTokensWithSecret(config, code, redirectUri, client
|
|
|
1308
1240
|
try {
|
|
1309
1241
|
const params = { grant_type: "authorization_code", code, client_id: config.clientId, client_secret: clientSecret, redirect_uri: redirectUri };
|
|
1310
1242
|
const tokenUrl = `${config.oauthBase}/token`;
|
|
1311
|
-
console.log(`B1Church token exchange request to: ${tokenUrl}`);
|
|
1312
|
-
console.log(` - client_id: ${config.clientId}`);
|
|
1313
|
-
console.log(` - redirect_uri: ${redirectUri}`);
|
|
1314
|
-
console.log(` - code: ${code.substring(0, 10)}...`);
|
|
1315
1243
|
const response = await fetch(tokenUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(params) });
|
|
1316
|
-
console.log(`B1Church token response status: ${response.status}`);
|
|
1317
1244
|
if (!response.ok) {
|
|
1318
|
-
const errorText = await response.text();
|
|
1319
|
-
console.error(`B1Church token exchange failed: ${response.status} - ${errorText}`);
|
|
1320
1245
|
return null;
|
|
1321
1246
|
}
|
|
1322
1247
|
const data = await response.json();
|
|
1323
|
-
console.log(`B1Church token exchange successful, got access_token: ${!!data.access_token}`);
|
|
1324
1248
|
return { access_token: data.access_token, refresh_token: data.refresh_token, token_type: data.token_type || "Bearer", created_at: Math.floor(Date.now() / 1e3), expires_in: data.expires_in, scope: data.scope || config.scopes.join(" ") };
|
|
1325
|
-
} catch
|
|
1326
|
-
console.error("B1Church token exchange error:", error);
|
|
1249
|
+
} catch {
|
|
1327
1250
|
return null;
|
|
1328
1251
|
}
|
|
1329
1252
|
}
|
|
@@ -1344,13 +1267,10 @@ async function initiateDeviceFlow(config) {
|
|
|
1344
1267
|
try {
|
|
1345
1268
|
const response = await fetch(`${config.oauthBase}${config.deviceAuthEndpoint}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ client_id: config.clientId, scope: config.scopes.join(" ") }) });
|
|
1346
1269
|
if (!response.ok) {
|
|
1347
|
-
const errorText = await response.text();
|
|
1348
|
-
console.error(`B1Church device authorize failed: ${response.status} - ${errorText}`);
|
|
1349
1270
|
return null;
|
|
1350
1271
|
}
|
|
1351
1272
|
return await response.json();
|
|
1352
|
-
} catch
|
|
1353
|
-
console.error("B1Church device flow initiation error:", error);
|
|
1273
|
+
} catch {
|
|
1354
1274
|
return null;
|
|
1355
1275
|
}
|
|
1356
1276
|
}
|
|
@@ -1454,13 +1374,13 @@ async function fetchFromProviderProxy(method, ministryId, providerId, path, auth
|
|
|
1454
1374
|
|
|
1455
1375
|
// src/providers/b1Church/converters.ts
|
|
1456
1376
|
function ministryToFolder(ministry) {
|
|
1457
|
-
return { type: "folder", id: ministry.id, title: ministry.name, path: "", image: ministry.photoUrl
|
|
1377
|
+
return { type: "folder", id: ministry.id, title: ministry.name, path: "", image: ministry.photoUrl };
|
|
1458
1378
|
}
|
|
1459
|
-
function planTypeToFolder(planType
|
|
1460
|
-
return { type: "folder", id: planType.id, title: planType.name, path: ""
|
|
1379
|
+
function planTypeToFolder(planType) {
|
|
1380
|
+
return { type: "folder", id: planType.id, title: planType.name, path: "" };
|
|
1461
1381
|
}
|
|
1462
1382
|
function planToFolder(plan) {
|
|
1463
|
-
return { type: "folder", id: plan.id, title: plan.name, path: "", isLeaf: true
|
|
1383
|
+
return { type: "folder", id: plan.id, title: plan.name, path: "", isLeaf: true };
|
|
1464
1384
|
}
|
|
1465
1385
|
async function planItemToPresentation(item, venueFeed) {
|
|
1466
1386
|
const itemType = item.itemType;
|
|
@@ -1509,7 +1429,7 @@ function getFilesFromVenueFeed(venueFeed, itemType, relatedId) {
|
|
|
1509
1429
|
return files;
|
|
1510
1430
|
}
|
|
1511
1431
|
function convertFeedFiles(feedFiles, thumbnailImage) {
|
|
1512
|
-
return feedFiles.filter((f) => f.url).map((f) => ({ type: "file", id: f.id || "", title: f.name || "", mediaType: detectMediaType(f.url || "", f.fileType), image: thumbnailImage, url: f.url || "",
|
|
1432
|
+
return feedFiles.filter((f) => f.url).map((f) => ({ type: "file", id: f.id || "", title: f.name || "", mediaType: detectMediaType(f.url || "", f.fileType), image: thumbnailImage, url: f.url || "", seconds: f.seconds, streamUrl: f.streamUrl }));
|
|
1513
1433
|
}
|
|
1514
1434
|
function planItemToInstruction(item) {
|
|
1515
1435
|
let itemType = item.itemType;
|
|
@@ -1528,9 +1448,9 @@ function planItemToInstruction(item) {
|
|
|
1528
1448
|
}
|
|
1529
1449
|
|
|
1530
1450
|
// src/providers/b1Church/B1ChurchProvider.ts
|
|
1531
|
-
var INTERNAL_PROVIDERS = ["b1church", "lessonschurch"];
|
|
1532
1451
|
function isExternalProviderItem(item) {
|
|
1533
|
-
if (!item.providerId ||
|
|
1452
|
+
if (!item.providerId || item.providerId === "b1church") return false;
|
|
1453
|
+
if (item.providerPath) return true;
|
|
1534
1454
|
const itemType = item.itemType || "";
|
|
1535
1455
|
return itemType.startsWith("provider");
|
|
1536
1456
|
}
|
|
@@ -1540,11 +1460,11 @@ var B1ChurchProvider = class {
|
|
|
1540
1460
|
this.id = "b1church";
|
|
1541
1461
|
this.name = "B1.Church";
|
|
1542
1462
|
this.logos = { light: "https://b1.church/b1-church-logo.png", dark: "https://b1.church/b1-church-logo.png" };
|
|
1543
|
-
this.config = { id: "b1church", name: "B1.Church", apiBase: `${API_BASE}/doing`, oauthBase: `${API_BASE}/membership/oauth`, clientId: "nsowldn58dk", scopes: ["plans"], supportsDeviceFlow: true, deviceAuthEndpoint: "/device/authorize", endpoints: { planItems: (churchId, planId) => `/
|
|
1463
|
+
this.config = { id: "b1church", name: "B1.Church", apiBase: `${API_BASE}/doing`, oauthBase: `${API_BASE}/membership/oauth`, clientId: "nsowldn58dk", scopes: ["plans"], supportsDeviceFlow: true, deviceAuthEndpoint: "/device/authorize", endpoints: { planItems: (churchId, planId) => `/planFeed/presenter/${churchId}/${planId}` } };
|
|
1544
1464
|
this.appBase = "https://admin.b1.church";
|
|
1545
1465
|
this.requiresAuth = true;
|
|
1546
1466
|
this.authTypes = ["oauth_pkce", "device_flow"];
|
|
1547
|
-
this.capabilities = { browse: true, presentations: true, playlist: true, instructions: true,
|
|
1467
|
+
this.capabilities = { browse: true, presentations: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
1548
1468
|
}
|
|
1549
1469
|
async apiRequest(path, authData) {
|
|
1550
1470
|
return this.apiHelper.apiRequest(this.config, this.id, path, authData);
|
|
@@ -1583,17 +1503,15 @@ var B1ChurchProvider = class {
|
|
|
1583
1503
|
const ministries = await fetchMinistries(authData);
|
|
1584
1504
|
return ministries.map((m) => {
|
|
1585
1505
|
const folder = ministryToFolder(m);
|
|
1586
|
-
|
|
1587
|
-
return { ...folder, path: `/ministries/${ministryId}` };
|
|
1506
|
+
return { ...folder, path: `/ministries/${m.id}` };
|
|
1588
1507
|
});
|
|
1589
1508
|
}
|
|
1590
1509
|
if (depth === 2) {
|
|
1591
1510
|
const ministryId = segments[1];
|
|
1592
1511
|
const planTypes = await fetchPlanTypes(ministryId, authData);
|
|
1593
1512
|
return planTypes.map((pt) => {
|
|
1594
|
-
const folder = planTypeToFolder(pt
|
|
1595
|
-
|
|
1596
|
-
return { ...folder, path: `/ministries/${ministryId}/${planTypeId}` };
|
|
1513
|
+
const folder = planTypeToFolder(pt);
|
|
1514
|
+
return { ...folder, path: `/ministries/${ministryId}/${pt.id}` };
|
|
1597
1515
|
});
|
|
1598
1516
|
}
|
|
1599
1517
|
if (depth === 3) {
|
|
@@ -1602,11 +1520,10 @@ var B1ChurchProvider = class {
|
|
|
1602
1520
|
const plans = await fetchPlans(planTypeId, authData);
|
|
1603
1521
|
return plans.map((p) => {
|
|
1604
1522
|
const folder = planToFolder(p);
|
|
1605
|
-
const planId = folder.providerData?.planId || folder.id;
|
|
1606
1523
|
return {
|
|
1607
1524
|
...folder,
|
|
1608
1525
|
isLeaf: true,
|
|
1609
|
-
path: `/ministries/${ministryId}/${planTypeId}/${
|
|
1526
|
+
path: `/ministries/${ministryId}/${planTypeId}/${p.id}`
|
|
1610
1527
|
};
|
|
1611
1528
|
});
|
|
1612
1529
|
}
|
|
@@ -1619,19 +1536,26 @@ var B1ChurchProvider = class {
|
|
|
1619
1536
|
const planId = segments[3];
|
|
1620
1537
|
const planTypeId = segments[2];
|
|
1621
1538
|
const plans = await fetchPlans(planTypeId, authData);
|
|
1622
|
-
const planFolder = plans.find((p) =>
|
|
1623
|
-
const folder2 = planToFolder(p);
|
|
1624
|
-
return folder2.providerData?.planId === planId || folder2.id === planId;
|
|
1625
|
-
});
|
|
1539
|
+
const planFolder = plans.find((p) => p.id === planId);
|
|
1626
1540
|
if (!planFolder) return null;
|
|
1627
|
-
const
|
|
1628
|
-
const
|
|
1629
|
-
const
|
|
1630
|
-
const venueId = providerData?.contentId;
|
|
1631
|
-
const planTitle = folder.title || "Plan";
|
|
1541
|
+
const churchId = planFolder.churchId;
|
|
1542
|
+
const venueId = planFolder.contentId;
|
|
1543
|
+
const planTitle = planFolder.name || "Plan";
|
|
1632
1544
|
if (!churchId) return null;
|
|
1633
1545
|
const pathFn = this.config.endpoints.planItems;
|
|
1634
1546
|
const planItems = await this.apiRequest(pathFn(churchId, planId), authData);
|
|
1547
|
+
if ((!planItems || planItems.length === 0) && planFolder.providerId && planFolder.providerPlanId) {
|
|
1548
|
+
const externalPlan = await fetchFromProviderProxy(
|
|
1549
|
+
"getPresentations",
|
|
1550
|
+
ministryId,
|
|
1551
|
+
planFolder.providerId,
|
|
1552
|
+
planFolder.providerPlanId,
|
|
1553
|
+
authData
|
|
1554
|
+
);
|
|
1555
|
+
if (externalPlan) {
|
|
1556
|
+
return { id: planId, name: planTitle, sections: externalPlan.sections, allFiles: externalPlan.allFiles };
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1635
1559
|
if (!planItems || !Array.isArray(planItems)) return null;
|
|
1636
1560
|
const venueFeed = venueId ? await fetchVenueFeed(venueId) : null;
|
|
1637
1561
|
const sections = [];
|
|
@@ -1648,10 +1572,25 @@ var B1ChurchProvider = class {
|
|
|
1648
1572
|
authData
|
|
1649
1573
|
);
|
|
1650
1574
|
if (externalPlan) {
|
|
1651
|
-
|
|
1652
|
-
|
|
1575
|
+
if (child.providerContentPath) {
|
|
1576
|
+
const externalInstructions = await fetchFromProviderProxy(
|
|
1577
|
+
"getInstructions",
|
|
1578
|
+
ministryId,
|
|
1579
|
+
child.providerId,
|
|
1580
|
+
child.providerPath,
|
|
1581
|
+
authData
|
|
1582
|
+
);
|
|
1583
|
+
const matchingPresentation = this.findPresentationByPath(externalPlan, externalInstructions, child.providerContentPath);
|
|
1584
|
+
if (matchingPresentation) {
|
|
1585
|
+
presentations.push(matchingPresentation);
|
|
1586
|
+
allFiles.push(...matchingPresentation.files);
|
|
1587
|
+
}
|
|
1588
|
+
} else {
|
|
1589
|
+
for (const section of externalPlan.sections) {
|
|
1590
|
+
presentations.push(...section.presentations);
|
|
1591
|
+
}
|
|
1592
|
+
allFiles.push(...externalPlan.allFiles);
|
|
1653
1593
|
}
|
|
1654
|
-
allFiles.push(...externalPlan.allFiles);
|
|
1655
1594
|
}
|
|
1656
1595
|
} else {
|
|
1657
1596
|
const presentation = await planItemToPresentation(child, venueFeed);
|
|
@@ -1674,49 +1613,74 @@ var B1ChurchProvider = class {
|
|
|
1674
1613
|
const planId = segments[3];
|
|
1675
1614
|
const planTypeId = segments[2];
|
|
1676
1615
|
const plans = await fetchPlans(planTypeId, authData);
|
|
1677
|
-
const planFolder = plans.find((p) =>
|
|
1678
|
-
const folder2 = planToFolder(p);
|
|
1679
|
-
return folder2.providerData?.planId === planId || folder2.id === planId;
|
|
1680
|
-
});
|
|
1616
|
+
const planFolder = plans.find((p) => p.id === planId);
|
|
1681
1617
|
if (!planFolder) return null;
|
|
1682
|
-
const
|
|
1683
|
-
const
|
|
1684
|
-
const churchId = providerData?.churchId;
|
|
1685
|
-
const planTitle = folder.title || "Plan";
|
|
1618
|
+
const churchId = planFolder.churchId;
|
|
1619
|
+
const planTitle = planFolder.name || "Plan";
|
|
1686
1620
|
if (!churchId) return null;
|
|
1687
1621
|
const pathFn = this.config.endpoints.planItems;
|
|
1688
1622
|
const planItems = await this.apiRequest(pathFn(churchId, planId), authData);
|
|
1623
|
+
if ((!planItems || planItems.length === 0) && planFolder.providerId && planFolder.providerPlanId) {
|
|
1624
|
+
const externalInstructions = await fetchFromProviderProxy(
|
|
1625
|
+
"getInstructions",
|
|
1626
|
+
ministryId,
|
|
1627
|
+
planFolder.providerId,
|
|
1628
|
+
planFolder.providerPlanId,
|
|
1629
|
+
authData
|
|
1630
|
+
);
|
|
1631
|
+
if (externalInstructions) {
|
|
1632
|
+
return { venueName: planTitle, items: externalInstructions.items };
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1689
1635
|
if (!planItems || !Array.isArray(planItems)) return null;
|
|
1690
1636
|
const processedItems = await this.processInstructionItems(planItems, ministryId, authData);
|
|
1691
1637
|
return { venueName: planTitle, items: processedItems };
|
|
1692
1638
|
}
|
|
1693
|
-
async getExpandedInstructions(path, authData) {
|
|
1694
|
-
return this.getInstructions(path, authData);
|
|
1695
|
-
}
|
|
1696
1639
|
async processInstructionItems(items, ministryId, authData) {
|
|
1697
1640
|
const result = [];
|
|
1698
1641
|
for (const item of items) {
|
|
1642
|
+
const instructionItem = planItemToInstruction(item);
|
|
1699
1643
|
if (isExternalProviderItem(item) && item.providerId && item.providerPath) {
|
|
1700
1644
|
const externalInstructions = await fetchFromProviderProxy(
|
|
1701
|
-
"
|
|
1645
|
+
"getInstructions",
|
|
1702
1646
|
ministryId,
|
|
1703
1647
|
item.providerId,
|
|
1704
1648
|
item.providerPath,
|
|
1705
1649
|
authData
|
|
1706
1650
|
);
|
|
1707
1651
|
if (externalInstructions) {
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1652
|
+
if (item.providerContentPath) {
|
|
1653
|
+
const matchingItem = this.findItemByPath(externalInstructions, item.providerContentPath);
|
|
1654
|
+
if (matchingItem?.children) {
|
|
1655
|
+
instructionItem.children = matchingItem.children;
|
|
1656
|
+
}
|
|
1657
|
+
} else {
|
|
1658
|
+
instructionItem.children = externalInstructions.items;
|
|
1659
|
+
}
|
|
1714
1660
|
}
|
|
1715
|
-
|
|
1661
|
+
} else if (item.children && item.children.length > 0) {
|
|
1662
|
+
instructionItem.children = await this.processInstructionItems(item.children, ministryId, authData);
|
|
1716
1663
|
}
|
|
1664
|
+
result.push(instructionItem);
|
|
1717
1665
|
}
|
|
1718
1666
|
return result;
|
|
1719
1667
|
}
|
|
1668
|
+
findItemByPath(instructions, path) {
|
|
1669
|
+
if (!path || !instructions) return null;
|
|
1670
|
+
return navigateToPath(instructions, path);
|
|
1671
|
+
}
|
|
1672
|
+
findPresentationByPath(plan, instructions, path) {
|
|
1673
|
+
if (!path || !instructions) return null;
|
|
1674
|
+
const item = navigateToPath(instructions, path);
|
|
1675
|
+
if (!item?.relatedId && !item?.id) return null;
|
|
1676
|
+
const presentationId = item.relatedId || item.id;
|
|
1677
|
+
for (const section of plan.sections) {
|
|
1678
|
+
for (const presentation of section.presentations) {
|
|
1679
|
+
if (presentation.id === presentationId) return presentation;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
return null;
|
|
1683
|
+
}
|
|
1720
1684
|
async getPlaylist(path, authData, resolution) {
|
|
1721
1685
|
const { segments, depth } = parsePath(path);
|
|
1722
1686
|
if (depth < 4 || segments[0] !== "ministries") return [];
|
|
@@ -1724,34 +1688,63 @@ var B1ChurchProvider = class {
|
|
|
1724
1688
|
const planId = segments[3];
|
|
1725
1689
|
const planTypeId = segments[2];
|
|
1726
1690
|
const plans = await fetchPlans(planTypeId, authData);
|
|
1727
|
-
const planFolder = plans.find((p) =>
|
|
1728
|
-
const folder2 = planToFolder(p);
|
|
1729
|
-
return folder2.providerData?.planId === planId || folder2.id === planId;
|
|
1730
|
-
});
|
|
1691
|
+
const planFolder = plans.find((p) => p.id === planId);
|
|
1731
1692
|
if (!planFolder) return [];
|
|
1732
|
-
const
|
|
1733
|
-
const
|
|
1734
|
-
const churchId = providerData?.churchId;
|
|
1735
|
-
const venueId = providerData?.contentId;
|
|
1693
|
+
const churchId = planFolder.churchId;
|
|
1694
|
+
const venueId = planFolder.contentId;
|
|
1736
1695
|
if (!churchId) return [];
|
|
1737
1696
|
const pathFn = this.config.endpoints.planItems;
|
|
1738
1697
|
const planItems = await this.apiRequest(pathFn(churchId, planId), authData);
|
|
1698
|
+
if ((!planItems || planItems.length === 0) && planFolder.providerId && planFolder.providerPlanId) {
|
|
1699
|
+
const externalFiles = await fetchFromProviderProxy(
|
|
1700
|
+
"getPlaylist",
|
|
1701
|
+
ministryId,
|
|
1702
|
+
planFolder.providerId,
|
|
1703
|
+
planFolder.providerPlanId,
|
|
1704
|
+
authData,
|
|
1705
|
+
resolution
|
|
1706
|
+
);
|
|
1707
|
+
return externalFiles || [];
|
|
1708
|
+
}
|
|
1739
1709
|
if (!planItems || !Array.isArray(planItems)) return [];
|
|
1740
1710
|
const venueFeed = venueId ? await fetchVenueFeed(venueId) : null;
|
|
1741
1711
|
const files = [];
|
|
1742
1712
|
for (const sectionItem of planItems) {
|
|
1743
1713
|
for (const child of sectionItem.children || []) {
|
|
1744
1714
|
if (isExternalProviderItem(child) && child.providerId && child.providerPath) {
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1715
|
+
if (child.providerContentPath) {
|
|
1716
|
+
const externalPlan = await fetchFromProviderProxy(
|
|
1717
|
+
"getPresentations",
|
|
1718
|
+
ministryId,
|
|
1719
|
+
child.providerId,
|
|
1720
|
+
child.providerPath,
|
|
1721
|
+
authData
|
|
1722
|
+
);
|
|
1723
|
+
const externalInstructions = await fetchFromProviderProxy(
|
|
1724
|
+
"getInstructions",
|
|
1725
|
+
ministryId,
|
|
1726
|
+
child.providerId,
|
|
1727
|
+
child.providerPath,
|
|
1728
|
+
authData
|
|
1729
|
+
);
|
|
1730
|
+
if (externalPlan) {
|
|
1731
|
+
const matchingPresentation = this.findPresentationByPath(externalPlan, externalInstructions, child.providerContentPath);
|
|
1732
|
+
if (matchingPresentation) {
|
|
1733
|
+
files.push(...matchingPresentation.files);
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
} else {
|
|
1737
|
+
const externalFiles = await fetchFromProviderProxy(
|
|
1738
|
+
"getPlaylist",
|
|
1739
|
+
ministryId,
|
|
1740
|
+
child.providerId,
|
|
1741
|
+
child.providerPath,
|
|
1742
|
+
authData,
|
|
1743
|
+
resolution
|
|
1744
|
+
);
|
|
1745
|
+
if (externalFiles) {
|
|
1746
|
+
files.push(...externalFiles);
|
|
1747
|
+
}
|
|
1755
1748
|
}
|
|
1756
1749
|
} else {
|
|
1757
1750
|
const itemType = child.itemType;
|
|
@@ -1777,7 +1770,7 @@ var PlanningCenterProvider = class {
|
|
|
1777
1770
|
this.ONE_WEEK_MS = 6048e5;
|
|
1778
1771
|
this.requiresAuth = true;
|
|
1779
1772
|
this.authTypes = ["oauth_pkce"];
|
|
1780
|
-
this.capabilities = { browse: true, presentations: true, playlist:
|
|
1773
|
+
this.capabilities = { browse: true, presentations: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
1781
1774
|
}
|
|
1782
1775
|
async apiRequest(path, auth) {
|
|
1783
1776
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
@@ -1839,8 +1832,7 @@ var PlanningCenterProvider = class {
|
|
|
1839
1832
|
id: plan.id,
|
|
1840
1833
|
title: plan.attributes.title || this.formatDate(plan.attributes.sort_date),
|
|
1841
1834
|
isLeaf: true,
|
|
1842
|
-
path: `${currentPath}/${plan.id}
|
|
1843
|
-
providerData: { sortDate: plan.attributes.sort_date }
|
|
1835
|
+
path: `${currentPath}/${plan.id}`
|
|
1844
1836
|
}));
|
|
1845
1837
|
}
|
|
1846
1838
|
async getPlanItems(serviceTypeId, planId, auth) {
|
|
@@ -1850,7 +1842,7 @@ var PlanningCenterProvider = class {
|
|
|
1850
1842
|
auth
|
|
1851
1843
|
);
|
|
1852
1844
|
if (!response?.data) return [];
|
|
1853
|
-
return response.data.map((item) => ({ type: "file", id: item.id, title: item.attributes.title || "", mediaType: "image", url: ""
|
|
1845
|
+
return response.data.map((item) => ({ type: "file", id: item.id, title: item.attributes.title || "", mediaType: "image", url: "" }));
|
|
1854
1846
|
}
|
|
1855
1847
|
async getPresentations(path, auth) {
|
|
1856
1848
|
const { segments, depth } = parsePath(path);
|
|
@@ -1958,6 +1950,43 @@ var PlanningCenterProvider = class {
|
|
|
1958
1950
|
const date = new Date(dateString);
|
|
1959
1951
|
return date.toISOString().slice(0, 10);
|
|
1960
1952
|
}
|
|
1953
|
+
async getPlaylist(path, auth, _resolution) {
|
|
1954
|
+
const plan = await this.getPresentations(path, auth);
|
|
1955
|
+
if (!plan) return null;
|
|
1956
|
+
return plan.allFiles.length > 0 ? plan.allFiles : null;
|
|
1957
|
+
}
|
|
1958
|
+
async getInstructions(path, auth) {
|
|
1959
|
+
const plan = await this.getPresentations(path, auth);
|
|
1960
|
+
if (!plan) return null;
|
|
1961
|
+
const sectionItems = plan.sections.map((section) => {
|
|
1962
|
+
const actionItems = section.presentations.map((pres) => {
|
|
1963
|
+
const fileItems = pres.files.map((file) => ({
|
|
1964
|
+
id: file.id,
|
|
1965
|
+
itemType: "file",
|
|
1966
|
+
label: file.title,
|
|
1967
|
+
embedUrl: file.url
|
|
1968
|
+
}));
|
|
1969
|
+
return {
|
|
1970
|
+
id: pres.id,
|
|
1971
|
+
itemType: "action",
|
|
1972
|
+
relatedId: pres.id,
|
|
1973
|
+
label: pres.name,
|
|
1974
|
+
description: pres.actionType,
|
|
1975
|
+
children: fileItems.length > 0 ? fileItems : void 0
|
|
1976
|
+
};
|
|
1977
|
+
});
|
|
1978
|
+
return {
|
|
1979
|
+
id: section.id,
|
|
1980
|
+
itemType: "section",
|
|
1981
|
+
label: section.name,
|
|
1982
|
+
children: actionItems
|
|
1983
|
+
};
|
|
1984
|
+
});
|
|
1985
|
+
return {
|
|
1986
|
+
venueName: plan.name,
|
|
1987
|
+
items: sectionItems
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1961
1990
|
};
|
|
1962
1991
|
|
|
1963
1992
|
// src/providers/bibleProject/data.json
|
|
@@ -4095,8 +4124,7 @@ var BibleProjectProvider = class {
|
|
|
4095
4124
|
browse: true,
|
|
4096
4125
|
presentations: true,
|
|
4097
4126
|
playlist: true,
|
|
4098
|
-
instructions:
|
|
4099
|
-
expandedInstructions: false,
|
|
4127
|
+
instructions: true,
|
|
4100
4128
|
mediaLicensing: false
|
|
4101
4129
|
};
|
|
4102
4130
|
}
|
|
@@ -4134,8 +4162,7 @@ var BibleProjectProvider = class {
|
|
|
4134
4162
|
title: video.title,
|
|
4135
4163
|
image: video.thumbnailUrl,
|
|
4136
4164
|
isLeaf: true,
|
|
4137
|
-
path: `${currentPath}/${video.id}
|
|
4138
|
-
providerData: { videoData: video }
|
|
4165
|
+
path: `${currentPath}/${video.id}`
|
|
4139
4166
|
}));
|
|
4140
4167
|
}
|
|
4141
4168
|
getVideoFile(collectionSlug, videoId) {
|
|
@@ -4186,6 +4213,50 @@ var BibleProjectProvider = class {
|
|
|
4186
4213
|
}
|
|
4187
4214
|
return null;
|
|
4188
4215
|
}
|
|
4216
|
+
async getInstructions(path, _auth) {
|
|
4217
|
+
const { segments, depth } = parsePath(path);
|
|
4218
|
+
if (depth < 1) return null;
|
|
4219
|
+
const collectionSlug = segments[0];
|
|
4220
|
+
const collection = this.data.collections.find((c) => this.slugify(c.name) === collectionSlug);
|
|
4221
|
+
if (!collection) return null;
|
|
4222
|
+
if (depth === 1) {
|
|
4223
|
+
const fileItems = collection.videos.map((video) => ({
|
|
4224
|
+
id: video.id,
|
|
4225
|
+
itemType: "file",
|
|
4226
|
+
label: video.title,
|
|
4227
|
+
embedUrl: video.videoUrl
|
|
4228
|
+
}));
|
|
4229
|
+
return {
|
|
4230
|
+
venueName: collection.name,
|
|
4231
|
+
items: [{
|
|
4232
|
+
id: this.slugify(collection.name),
|
|
4233
|
+
itemType: "section",
|
|
4234
|
+
label: "Videos",
|
|
4235
|
+
children: fileItems
|
|
4236
|
+
}]
|
|
4237
|
+
};
|
|
4238
|
+
}
|
|
4239
|
+
if (depth === 2) {
|
|
4240
|
+
const videoId = segments[1];
|
|
4241
|
+
const video = collection.videos.find((v) => v.id === videoId);
|
|
4242
|
+
if (!video) return null;
|
|
4243
|
+
return {
|
|
4244
|
+
venueName: video.title,
|
|
4245
|
+
items: [{
|
|
4246
|
+
id: "main",
|
|
4247
|
+
itemType: "section",
|
|
4248
|
+
label: "Content",
|
|
4249
|
+
children: [{
|
|
4250
|
+
id: video.id,
|
|
4251
|
+
itemType: "file",
|
|
4252
|
+
label: video.title,
|
|
4253
|
+
embedUrl: video.videoUrl
|
|
4254
|
+
}]
|
|
4255
|
+
}]
|
|
4256
|
+
};
|
|
4257
|
+
}
|
|
4258
|
+
return null;
|
|
4259
|
+
}
|
|
4189
4260
|
slugify(text) {
|
|
4190
4261
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
4191
4262
|
}
|
|
@@ -11722,8 +11793,7 @@ var HighVoltageKidsProvider = class {
|
|
|
11722
11793
|
browse: true,
|
|
11723
11794
|
presentations: true,
|
|
11724
11795
|
playlist: true,
|
|
11725
|
-
instructions:
|
|
11726
|
-
expandedInstructions: true,
|
|
11796
|
+
instructions: true,
|
|
11727
11797
|
mediaLicensing: false
|
|
11728
11798
|
};
|
|
11729
11799
|
}
|
|
@@ -11765,8 +11835,7 @@ var HighVoltageKidsProvider = class {
|
|
|
11765
11835
|
id: study.id,
|
|
11766
11836
|
title: study.name,
|
|
11767
11837
|
image: study.image || void 0,
|
|
11768
|
-
path: `${currentPath}/${study.id}
|
|
11769
|
-
providerData: { studyData: study }
|
|
11838
|
+
path: `${currentPath}/${study.id}`
|
|
11770
11839
|
}));
|
|
11771
11840
|
}
|
|
11772
11841
|
getLessonFolders(collectionSlug, studyId, currentPath) {
|
|
@@ -11780,8 +11849,7 @@ var HighVoltageKidsProvider = class {
|
|
|
11780
11849
|
title: lesson.name,
|
|
11781
11850
|
image: lesson.image || void 0,
|
|
11782
11851
|
isLeaf: true,
|
|
11783
|
-
path: `${currentPath}/${lesson.id}
|
|
11784
|
-
providerData: { lessonData: lesson, studyName: study.name }
|
|
11852
|
+
path: `${currentPath}/${lesson.id}`
|
|
11785
11853
|
}));
|
|
11786
11854
|
}
|
|
11787
11855
|
getLessonFiles(collectionSlug, studyId, lessonId) {
|
|
@@ -11851,7 +11919,7 @@ var HighVoltageKidsProvider = class {
|
|
|
11851
11919
|
}
|
|
11852
11920
|
return null;
|
|
11853
11921
|
}
|
|
11854
|
-
async
|
|
11922
|
+
async getInstructions(path, _auth) {
|
|
11855
11923
|
const { segments, depth } = parsePath(path);
|
|
11856
11924
|
if (depth < 2) return null;
|
|
11857
11925
|
const collectionSlug = segments[0];
|
|
@@ -11862,7 +11930,10 @@ var HighVoltageKidsProvider = class {
|
|
|
11862
11930
|
if (!study) return null;
|
|
11863
11931
|
if (depth === 2) {
|
|
11864
11932
|
const lessonItems = study.lessons.map((lesson) => {
|
|
11865
|
-
const fileItems = lesson.files.map((file) =>
|
|
11933
|
+
const fileItems = lesson.files.map((file) => {
|
|
11934
|
+
const seconds = estimateDuration(file.mediaType);
|
|
11935
|
+
return { id: file.id, itemType: "file", label: file.title, seconds, embedUrl: file.url };
|
|
11936
|
+
});
|
|
11866
11937
|
return { id: lesson.id, itemType: "action", label: lesson.name, description: "play", children: fileItems };
|
|
11867
11938
|
});
|
|
11868
11939
|
return { venueName: study.name, items: [{ id: study.id, itemType: "header", label: study.name, children: [{ id: "main", itemType: "section", label: "Content", children: lessonItems }] }] };
|
|
@@ -11886,12 +11957,16 @@ var HighVoltageKidsProvider = class {
|
|
|
11886
11957
|
let currentBaseName = null;
|
|
11887
11958
|
const flushGroup = () => {
|
|
11888
11959
|
if (currentGroup.length === 0) return;
|
|
11889
|
-
const children = currentGroup.map((file) =>
|
|
11890
|
-
|
|
11891
|
-
|
|
11892
|
-
|
|
11893
|
-
|
|
11894
|
-
|
|
11960
|
+
const children = currentGroup.map((file) => {
|
|
11961
|
+
const seconds = estimateDuration(file.mediaType);
|
|
11962
|
+
return {
|
|
11963
|
+
id: file.id,
|
|
11964
|
+
itemType: "file",
|
|
11965
|
+
label: file.title,
|
|
11966
|
+
seconds,
|
|
11967
|
+
embedUrl: file.url
|
|
11968
|
+
};
|
|
11969
|
+
});
|
|
11895
11970
|
const label = currentGroup.length > 1 && currentBaseName ? currentBaseName : currentGroup[0].title;
|
|
11896
11971
|
actionItems.push({
|
|
11897
11972
|
id: currentGroup[0].id + "-action",
|
|
@@ -12022,7 +12097,7 @@ function getAvailableProviders(ids) {
|
|
|
12022
12097
|
implemented: false,
|
|
12023
12098
|
requiresAuth: false,
|
|
12024
12099
|
authTypes: [],
|
|
12025
|
-
capabilities: { browse: false, presentations: false, playlist: false, instructions: false,
|
|
12100
|
+
capabilities: { browse: false, presentations: false, playlist: false, instructions: false, mediaLicensing: false }
|
|
12026
12101
|
}));
|
|
12027
12102
|
const all = [...implemented, ...comingSoon];
|
|
12028
12103
|
if (ids && ids.length > 0) {
|
|
@@ -12033,13 +12108,14 @@ function getAvailableProviders(ids) {
|
|
|
12033
12108
|
}
|
|
12034
12109
|
|
|
12035
12110
|
// src/index.ts
|
|
12036
|
-
var VERSION = "0.0.
|
|
12111
|
+
var VERSION = "0.0.4";
|
|
12037
12112
|
export {
|
|
12038
12113
|
APlayProvider,
|
|
12039
12114
|
ApiHelper,
|
|
12040
12115
|
B1ChurchProvider,
|
|
12041
12116
|
BibleProjectProvider,
|
|
12042
12117
|
ContentProvider,
|
|
12118
|
+
DEFAULT_DURATION_CONFIG,
|
|
12043
12119
|
DeviceFlowHelper,
|
|
12044
12120
|
FormatConverters_exports as FormatConverters,
|
|
12045
12121
|
FormatResolver,
|
|
@@ -12052,12 +12128,16 @@ export {
|
|
|
12052
12128
|
VERSION,
|
|
12053
12129
|
appendToPath,
|
|
12054
12130
|
buildPath,
|
|
12055
|
-
|
|
12131
|
+
countWords,
|
|
12056
12132
|
createFile,
|
|
12057
12133
|
createFolder,
|
|
12058
12134
|
detectMediaType,
|
|
12135
|
+
estimateDuration,
|
|
12136
|
+
estimateImageDuration,
|
|
12137
|
+
estimateTextDuration,
|
|
12059
12138
|
expandedInstructionsToPlaylist,
|
|
12060
12139
|
expandedInstructionsToPresentations,
|
|
12140
|
+
generatePath,
|
|
12061
12141
|
getAllProviders,
|
|
12062
12142
|
getAvailableProviders,
|
|
12063
12143
|
getProvider,
|
|
@@ -12067,12 +12147,12 @@ export {
|
|
|
12067
12147
|
instructionsToPresentations,
|
|
12068
12148
|
isContentFile,
|
|
12069
12149
|
isContentFolder,
|
|
12150
|
+
navigateToPath,
|
|
12070
12151
|
parsePath,
|
|
12071
12152
|
playlistToExpandedInstructions,
|
|
12072
12153
|
playlistToInstructions,
|
|
12073
12154
|
playlistToPresentations,
|
|
12074
12155
|
presentationsToExpandedInstructions,
|
|
12075
|
-
presentationsToInstructions,
|
|
12076
12156
|
presentationsToPlaylist,
|
|
12077
12157
|
registerProvider
|
|
12078
12158
|
};
|