@churchapps/content-providers 0.4.1 → 0.5.0
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/README.md +14 -16
- package/dist/index.cjs +442 -840
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -48
- package/dist/index.d.ts +70 -48
- package/dist/index.js +437 -839
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -22,13 +22,16 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
APlayProvider: () => APlayProvider,
|
|
24
24
|
B1ChurchProvider: () => B1ChurchProvider,
|
|
25
|
+
BaseProvider: () => BaseProvider,
|
|
25
26
|
BibleProjectProvider: () => BibleProjectProvider,
|
|
26
27
|
CbnProvider: () => CbnProvider,
|
|
28
|
+
DeviceFlowHelper: () => DeviceFlowHelper,
|
|
27
29
|
DropboxProvider: () => DropboxProvider,
|
|
28
30
|
HighVoltageKidsProvider: () => HighVoltageKidsProvider,
|
|
29
31
|
JesusFilmProvider: () => JesusFilmProvider,
|
|
30
32
|
LessonsChurchProvider: () => LessonsChurchProvider,
|
|
31
33
|
LifeChurchProvider: () => LifeChurchProvider,
|
|
34
|
+
OAuthHelper: () => OAuthHelper,
|
|
32
35
|
PlanningCenterProvider: () => PlanningCenterProvider,
|
|
33
36
|
SignPresenterProvider: () => SignPresenterProvider,
|
|
34
37
|
TokenHelper: () => TokenHelper,
|
|
@@ -40,7 +43,8 @@ __export(index_exports, {
|
|
|
40
43
|
isContentFile: () => isContentFile,
|
|
41
44
|
isContentFolder: () => isContentFolder,
|
|
42
45
|
navigateToPath: () => navigateToPath,
|
|
43
|
-
registerProvider: () => registerProvider
|
|
46
|
+
registerProvider: () => registerProvider,
|
|
47
|
+
toAuthData: () => toAuthData
|
|
44
48
|
});
|
|
45
49
|
module.exports = __toCommonJS(index_exports);
|
|
46
50
|
|
|
@@ -64,33 +68,81 @@ function navigateToPath(instructions, path) {
|
|
|
64
68
|
return current;
|
|
65
69
|
}
|
|
66
70
|
|
|
71
|
+
// src/helpers/TokenHelper.ts
|
|
72
|
+
function toAuthData(data, fallbacks) {
|
|
73
|
+
return {
|
|
74
|
+
access_token: data.access_token,
|
|
75
|
+
refresh_token: data.refresh_token || fallbacks?.refreshToken || "",
|
|
76
|
+
token_type: data.token_type || "Bearer",
|
|
77
|
+
created_at: Math.floor(Date.now() / 1e3),
|
|
78
|
+
expires_in: data.expires_in,
|
|
79
|
+
scope: data.scope || fallbacks?.scope || ""
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
var TokenHelper = class {
|
|
83
|
+
isAuthValid(auth) {
|
|
84
|
+
if (!auth) return false;
|
|
85
|
+
return !this.isTokenExpired(auth);
|
|
86
|
+
}
|
|
87
|
+
isTokenExpired(auth) {
|
|
88
|
+
if (!auth.created_at || !auth.expires_in) return true;
|
|
89
|
+
const expiresAt = (auth.created_at + auth.expires_in) * 1e3;
|
|
90
|
+
return Date.now() > expiresAt - 5 * 60 * 1e3;
|
|
91
|
+
}
|
|
92
|
+
async refreshToken(config, auth) {
|
|
93
|
+
if (!auth.refresh_token) return null;
|
|
94
|
+
try {
|
|
95
|
+
const params = new URLSearchParams({
|
|
96
|
+
grant_type: "refresh_token",
|
|
97
|
+
refresh_token: auth.refresh_token,
|
|
98
|
+
client_id: config.clientId
|
|
99
|
+
});
|
|
100
|
+
const response = await fetch(`${config.oauthBase}/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: params.toString() });
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
console.warn(`[TokenHelper] Token refresh failed: ${response.status} ${response.statusText}`);
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const data = await response.json();
|
|
106
|
+
return toAuthData(data, { refreshToken: auth.refresh_token, scope: auth.scope });
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
67
113
|
// src/helpers/OAuthHelper.ts
|
|
114
|
+
function generateCodeVerifier() {
|
|
115
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
116
|
+
const length = 64;
|
|
117
|
+
const array = new Uint8Array(length);
|
|
118
|
+
crypto.getRandomValues(array);
|
|
119
|
+
let result = "";
|
|
120
|
+
for (let i = 0; i < length; i++) {
|
|
121
|
+
result += chars.charAt(array[i] % chars.length);
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
async function generateCodeChallenge(verifier) {
|
|
126
|
+
const encoder = new TextEncoder();
|
|
127
|
+
const data = encoder.encode(verifier);
|
|
128
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
129
|
+
const hashArray = new Uint8Array(hashBuffer);
|
|
130
|
+
let binary = "";
|
|
131
|
+
for (let i = 0; i < hashArray.length; i++) {
|
|
132
|
+
binary += String.fromCharCode(hashArray[i]);
|
|
133
|
+
}
|
|
134
|
+
const base64 = btoa(binary);
|
|
135
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
136
|
+
}
|
|
68
137
|
var OAuthHelper = class {
|
|
69
138
|
generateCodeVerifier() {
|
|
70
|
-
|
|
71
|
-
const length = 64;
|
|
72
|
-
const array = new Uint8Array(length);
|
|
73
|
-
crypto.getRandomValues(array);
|
|
74
|
-
let result = "";
|
|
75
|
-
for (let i = 0; i < length; i++) {
|
|
76
|
-
result += chars.charAt(array[i] % chars.length);
|
|
77
|
-
}
|
|
78
|
-
return result;
|
|
139
|
+
return generateCodeVerifier();
|
|
79
140
|
}
|
|
80
141
|
async generateCodeChallenge(verifier) {
|
|
81
|
-
|
|
82
|
-
const data = encoder.encode(verifier);
|
|
83
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
84
|
-
const hashArray = new Uint8Array(hashBuffer);
|
|
85
|
-
let binary = "";
|
|
86
|
-
for (let i = 0; i < hashArray.length; i++) {
|
|
87
|
-
binary += String.fromCharCode(hashArray[i]);
|
|
88
|
-
}
|
|
89
|
-
const base64 = btoa(binary);
|
|
90
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
142
|
+
return generateCodeChallenge(verifier);
|
|
91
143
|
}
|
|
92
144
|
async buildAuthUrl(config, codeVerifier, redirectUri, state) {
|
|
93
|
-
const codeChallenge = await
|
|
145
|
+
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
94
146
|
const params = new URLSearchParams({
|
|
95
147
|
response_type: "code",
|
|
96
148
|
client_id: config.clientId,
|
|
@@ -117,39 +169,7 @@ var OAuthHelper = class {
|
|
|
117
169
|
return null;
|
|
118
170
|
}
|
|
119
171
|
const data = await response.json();
|
|
120
|
-
return
|
|
121
|
-
} catch {
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
// src/helpers/TokenHelper.ts
|
|
128
|
-
var TokenHelper = class {
|
|
129
|
-
isAuthValid(auth) {
|
|
130
|
-
if (!auth) return false;
|
|
131
|
-
return !this.isTokenExpired(auth);
|
|
132
|
-
}
|
|
133
|
-
isTokenExpired(auth) {
|
|
134
|
-
if (!auth.created_at || !auth.expires_in) return true;
|
|
135
|
-
const expiresAt = (auth.created_at + auth.expires_in) * 1e3;
|
|
136
|
-
return Date.now() > expiresAt - 5 * 60 * 1e3;
|
|
137
|
-
}
|
|
138
|
-
async refreshToken(config, auth) {
|
|
139
|
-
if (!auth.refresh_token) return null;
|
|
140
|
-
try {
|
|
141
|
-
const params = new URLSearchParams({
|
|
142
|
-
grant_type: "refresh_token",
|
|
143
|
-
refresh_token: auth.refresh_token,
|
|
144
|
-
client_id: config.clientId
|
|
145
|
-
});
|
|
146
|
-
const response = await fetch(`${config.oauthBase}/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: params.toString() });
|
|
147
|
-
if (!response.ok) {
|
|
148
|
-
console.warn(`[TokenHelper] Token refresh failed: ${response.status} ${response.statusText}`);
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
const data = await response.json();
|
|
152
|
-
return { access_token: data.access_token, refresh_token: data.refresh_token || auth.refresh_token, token_type: data.token_type || "Bearer", created_at: Math.floor(Date.now() / 1e3), expires_in: data.expires_in, scope: data.scope || auth.scope };
|
|
172
|
+
return toAuthData(data, { scope: config.scopes.join(" ") });
|
|
153
173
|
} catch {
|
|
154
174
|
return null;
|
|
155
175
|
}
|
|
@@ -176,7 +196,7 @@ var DeviceFlowHelper = class {
|
|
|
176
196
|
const response = await fetch(`${config.oauthBase}/token`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ grant_type: "urn:ietf:params:oauth:grant-type:device_code", device_code: deviceCode, client_id: config.clientId }) });
|
|
177
197
|
if (response.ok) {
|
|
178
198
|
const data = await response.json();
|
|
179
|
-
return
|
|
199
|
+
return toAuthData(data, { scope: config.scopes.join(" ") });
|
|
180
200
|
}
|
|
181
201
|
const errorData = await response.json();
|
|
182
202
|
switch (errorData.error) {
|
|
@@ -226,6 +246,19 @@ var ApiHelper = class {
|
|
|
226
246
|
}
|
|
227
247
|
};
|
|
228
248
|
|
|
249
|
+
// src/providers/BaseProvider.ts
|
|
250
|
+
var BaseProvider = class {
|
|
251
|
+
constructor() {
|
|
252
|
+
this.apiHelper = new ApiHelper();
|
|
253
|
+
}
|
|
254
|
+
apiRequest(path, auth, method = "GET", body) {
|
|
255
|
+
return this.apiHelper.apiRequest(this.config, this.id, path, auth, method, body);
|
|
256
|
+
}
|
|
257
|
+
supportsDeviceFlow() {
|
|
258
|
+
return !!this.config.supportsDeviceFlow && !!this.config.deviceAuthEndpoint;
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
229
262
|
// src/pathUtils.ts
|
|
230
263
|
function parsePath(path) {
|
|
231
264
|
if (!path || path === "/" || path === "") {
|
|
@@ -239,26 +272,6 @@ function getSegment(path, index) {
|
|
|
239
272
|
return segments[index] ?? null;
|
|
240
273
|
}
|
|
241
274
|
|
|
242
|
-
// src/providers/aPlay/APlayApi.ts
|
|
243
|
-
var API_BASE = "https://api-prod.amazingkids.app";
|
|
244
|
-
async function checkMediaLicense(mediaId, auth) {
|
|
245
|
-
if (!auth) return null;
|
|
246
|
-
try {
|
|
247
|
-
const url = `${API_BASE}/prod/reports/media/license-check`;
|
|
248
|
-
const response = await fetch(url, { method: "POST", headers: { "Authorization": `Bearer ${auth.access_token}`, "Content-Type": "application/json", "Accept": "application/json" }, body: JSON.stringify({ mediaIds: [mediaId] }) });
|
|
249
|
-
if (!response.ok) return null;
|
|
250
|
-
const data = await response.json();
|
|
251
|
-
const licenseData = Array.isArray(data) ? data : data.data || [];
|
|
252
|
-
const result = licenseData.find((item) => item.mediaId === mediaId);
|
|
253
|
-
if (result?.isLicensed) {
|
|
254
|
-
return { mediaId, status: "valid", message: "Media is licensed for playback", expiresAt: result.expiresAt };
|
|
255
|
-
}
|
|
256
|
-
return { mediaId, status: "not_licensed", message: "Media is not licensed" };
|
|
257
|
-
} catch {
|
|
258
|
-
return { mediaId, status: "unknown", message: "Unable to verify license status" };
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
275
|
// src/utils.ts
|
|
263
276
|
var IMAGE_DURATION_SECONDS = 15;
|
|
264
277
|
function slugify(text) {
|
|
@@ -296,6 +309,31 @@ function createFile(id, title, url, options) {
|
|
|
296
309
|
function generateId() {
|
|
297
310
|
return "gen-" + Math.random().toString(36).substring(2, 11);
|
|
298
311
|
}
|
|
312
|
+
function fileToActionItem(file) {
|
|
313
|
+
return {
|
|
314
|
+
id: file.id + "-action",
|
|
315
|
+
itemType: "action",
|
|
316
|
+
label: file.title,
|
|
317
|
+
actionType: "play",
|
|
318
|
+
seconds: file.seconds || void 0,
|
|
319
|
+
children: [
|
|
320
|
+
{
|
|
321
|
+
id: file.id,
|
|
322
|
+
itemType: "file",
|
|
323
|
+
label: file.title,
|
|
324
|
+
seconds: file.seconds || void 0,
|
|
325
|
+
downloadUrl: file.downloadUrl || file.url,
|
|
326
|
+
thumbnail: file.thumbnail,
|
|
327
|
+
mediaType: file.mediaType
|
|
328
|
+
}
|
|
329
|
+
]
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function filesToInstructions(name, files, section) {
|
|
333
|
+
const actions = files.map(fileToActionItem);
|
|
334
|
+
const items = section ? [{ id: section.id, itemType: "section", label: section.label ?? name, children: actions }] : actions;
|
|
335
|
+
return { name, items };
|
|
336
|
+
}
|
|
299
337
|
function instructionsToPlaylist(instructions) {
|
|
300
338
|
const files = [];
|
|
301
339
|
function extractFiles(items) {
|
|
@@ -310,6 +348,26 @@ function instructionsToPlaylist(instructions) {
|
|
|
310
348
|
return files;
|
|
311
349
|
}
|
|
312
350
|
|
|
351
|
+
// src/providers/aPlay/APlayApi.ts
|
|
352
|
+
var API_BASE = "https://api-prod.amazingkids.app";
|
|
353
|
+
async function checkMediaLicense(mediaId, auth) {
|
|
354
|
+
if (!auth) return null;
|
|
355
|
+
try {
|
|
356
|
+
const url = `${API_BASE}/prod/reports/media/license-check`;
|
|
357
|
+
const response = await fetch(url, { method: "POST", headers: { "Authorization": `Bearer ${auth.access_token}`, "Content-Type": "application/json", "Accept": "application/json" }, body: JSON.stringify({ mediaIds: [mediaId] }) });
|
|
358
|
+
if (!response.ok) return null;
|
|
359
|
+
const data = await response.json();
|
|
360
|
+
const licenseData = Array.isArray(data) ? data : data.data || [];
|
|
361
|
+
const result = licenseData.find((item) => item.mediaId === mediaId);
|
|
362
|
+
if (result?.isLicensed) {
|
|
363
|
+
return { mediaId, status: "valid", message: "Media is licensed for playback", expiresAt: result.expiresAt };
|
|
364
|
+
}
|
|
365
|
+
return { mediaId, status: "not_licensed", message: "Media is not licensed" };
|
|
366
|
+
} catch {
|
|
367
|
+
return { mediaId, status: "unknown", message: "Unable to verify license status" };
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
313
371
|
// src/providers/aPlay/APlayConverters.ts
|
|
314
372
|
function extractLibraryId(path) {
|
|
315
373
|
const { segments, depth } = parsePath(path);
|
|
@@ -376,25 +434,6 @@ function convertProductsToFolders(products, currentPath) {
|
|
|
376
434
|
return createFolder(id, p.title || p.name, `${currentPath}/products/${id}`, p.image);
|
|
377
435
|
});
|
|
378
436
|
}
|
|
379
|
-
function convertFilesToInstructions(files, _libraryId) {
|
|
380
|
-
const items = files.map((file) => ({
|
|
381
|
-
id: file.id + "-action",
|
|
382
|
-
itemType: "action",
|
|
383
|
-
label: file.title,
|
|
384
|
-
actionType: "play",
|
|
385
|
-
children: [
|
|
386
|
-
{
|
|
387
|
-
id: file.id,
|
|
388
|
-
itemType: "file",
|
|
389
|
-
label: file.title,
|
|
390
|
-
seconds: file.seconds,
|
|
391
|
-
downloadUrl: file.url,
|
|
392
|
-
thumbnail: file.thumbnail
|
|
393
|
-
}
|
|
394
|
-
]
|
|
395
|
-
}));
|
|
396
|
-
return { name: "Library", items };
|
|
397
|
-
}
|
|
398
437
|
|
|
399
438
|
// src/providers/aPlay/APlayProvider.ts
|
|
400
439
|
function extractArray(response, ...keys) {
|
|
@@ -407,20 +446,17 @@ function extractArray(response, ...keys) {
|
|
|
407
446
|
}
|
|
408
447
|
return [];
|
|
409
448
|
}
|
|
410
|
-
var APlayProvider = class {
|
|
449
|
+
var APlayProvider = class extends BaseProvider {
|
|
411
450
|
constructor() {
|
|
412
|
-
|
|
451
|
+
super(...arguments);
|
|
413
452
|
this.id = "aplay";
|
|
414
453
|
this.name = "APlay";
|
|
415
454
|
this.logos = { light: "https://www.joinamazing.com/_assets/v11/3ba846c5afd7e73d27bc4d87b63d423e7ae2dc73.svg", dark: "https://www.joinamazing.com/_assets/v11/3ba846c5afd7e73d27bc4d87b63d423e7ae2dc73.svg" };
|
|
416
|
-
this.config = { id: "aplay", name: "APlay", apiBase: API_BASE, oauthBase: "https://api.joinamazing.com/prod/aims/oauth", clientId: "xFJFq7yNYuXXXMx0YBiQ", scopes: ["openid", "profile", "email"]
|
|
455
|
+
this.config = { id: "aplay", name: "APlay", apiBase: API_BASE, oauthBase: "https://api.joinamazing.com/prod/aims/oauth", clientId: "xFJFq7yNYuXXXMx0YBiQ", scopes: ["openid", "profile", "email"] };
|
|
417
456
|
this.requiresAuth = true;
|
|
418
457
|
this.authTypes = ["oauth_pkce"];
|
|
419
458
|
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: true };
|
|
420
459
|
}
|
|
421
|
-
async apiRequest(path, auth) {
|
|
422
|
-
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
423
|
-
}
|
|
424
460
|
async browse(path, auth) {
|
|
425
461
|
const { segments, depth } = parsePath(path);
|
|
426
462
|
if (depth === 0) {
|
|
@@ -436,12 +472,12 @@ var APlayProvider = class {
|
|
|
436
472
|
return [];
|
|
437
473
|
}
|
|
438
474
|
async getModules(auth) {
|
|
439
|
-
const response = await this.apiRequest(
|
|
475
|
+
const response = await this.apiRequest("/prod/curriculum/modules", auth);
|
|
440
476
|
const modules = extractArray(response, "data", "modules");
|
|
441
477
|
return convertModulesToFolders(modules);
|
|
442
478
|
}
|
|
443
479
|
async getModuleContent(moduleId, currentPath, auth) {
|
|
444
|
-
const response = await this.apiRequest(
|
|
480
|
+
const response = await this.apiRequest("/prod/curriculum/modules", auth);
|
|
445
481
|
const modules = extractArray(response, "data", "modules");
|
|
446
482
|
const module2 = modules.find((m) => (m.id || m.moduleId) === moduleId);
|
|
447
483
|
if (!module2) return [];
|
|
@@ -457,14 +493,12 @@ var APlayProvider = class {
|
|
|
457
493
|
}
|
|
458
494
|
}
|
|
459
495
|
async getLibraryFolders(productId, currentPath, auth) {
|
|
460
|
-
const
|
|
461
|
-
const response = await this.apiRequest(pathFn(productId), auth);
|
|
496
|
+
const response = await this.apiRequest(`/prod/curriculum/modules/products/${productId}/libraries`, auth);
|
|
462
497
|
const libraries = extractArray(response, "data", "libraries");
|
|
463
498
|
return convertLibrariesToFolders(libraries, currentPath);
|
|
464
499
|
}
|
|
465
500
|
async getMediaFiles(libraryId, auth) {
|
|
466
|
-
const
|
|
467
|
-
const response = await this.apiRequest(pathFn(libraryId), auth);
|
|
501
|
+
const response = await this.apiRequest(`/prod/creators/libraries/${libraryId}/media`, auth);
|
|
468
502
|
const mediaItems = extractArray(response, "data", "media");
|
|
469
503
|
return convertMediaToFiles(mediaItems);
|
|
470
504
|
}
|
|
@@ -479,63 +513,47 @@ var APlayProvider = class {
|
|
|
479
513
|
if (!libraryId) return null;
|
|
480
514
|
const files = await this.getMediaFiles(libraryId, auth);
|
|
481
515
|
if (files.length === 0) return null;
|
|
482
|
-
return
|
|
516
|
+
return filesToInstructions("Library", files);
|
|
483
517
|
}
|
|
484
518
|
async checkMediaLicense(mediaId, auth) {
|
|
485
519
|
return checkMediaLicense(mediaId, auth);
|
|
486
520
|
}
|
|
487
|
-
supportsDeviceFlow() {
|
|
488
|
-
return false;
|
|
489
|
-
}
|
|
490
521
|
};
|
|
491
522
|
|
|
492
523
|
// src/providers/signPresenter/SignPresenterProvider.ts
|
|
493
|
-
var SignPresenterProvider = class {
|
|
524
|
+
var SignPresenterProvider = class extends BaseProvider {
|
|
494
525
|
constructor() {
|
|
495
|
-
|
|
526
|
+
super(...arguments);
|
|
496
527
|
this.oauthHelper = new OAuthHelper();
|
|
497
528
|
this.deviceFlowHelper = new DeviceFlowHelper();
|
|
498
529
|
this.id = "signpresenter";
|
|
499
530
|
this.name = "SignPresenter";
|
|
500
531
|
this.logos = { light: "https://signpresenter.com/files/shared/images/logo.png", dark: "https://signpresenter.com/files/shared/images/logo.png" };
|
|
501
|
-
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"
|
|
532
|
+
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" };
|
|
502
533
|
this.requiresAuth = true;
|
|
503
534
|
this.authTypes = ["oauth_pkce", "device_flow"];
|
|
504
535
|
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
505
536
|
}
|
|
506
|
-
async apiRequest(path, auth) {
|
|
507
|
-
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
508
|
-
}
|
|
509
537
|
async browse(path, auth) {
|
|
510
538
|
const { segments, depth } = parsePath(path);
|
|
511
539
|
if (depth === 0) {
|
|
512
|
-
return [
|
|
513
|
-
{
|
|
514
|
-
type: "folder",
|
|
515
|
-
id: "playlists-root",
|
|
516
|
-
title: "Playlists",
|
|
517
|
-
path: "/playlists"
|
|
518
|
-
}
|
|
519
|
-
];
|
|
520
|
-
}
|
|
521
|
-
const root = segments[0];
|
|
522
|
-
if (root !== "playlists") return [];
|
|
523
|
-
if (depth === 1) {
|
|
524
|
-
return this.getPlaylists(auth);
|
|
525
|
-
}
|
|
526
|
-
if (depth === 2) {
|
|
527
|
-
const playlistId = segments[1];
|
|
528
|
-
return this.getMessages(playlistId, auth);
|
|
540
|
+
return [{ type: "folder", id: "playlists-root", title: "Playlists", path: "/playlists" }];
|
|
529
541
|
}
|
|
542
|
+
if (segments[0] !== "playlists") return [];
|
|
543
|
+
if (depth === 1) return this.getPlaylists(auth);
|
|
544
|
+
if (depth === 2) return this.getMessages(segments[1], auth);
|
|
530
545
|
return [];
|
|
531
546
|
}
|
|
547
|
+
extractList(response, key) {
|
|
548
|
+
if (Array.isArray(response)) return response;
|
|
549
|
+
const record = response;
|
|
550
|
+
const list = record?.data || record?.[key] || [];
|
|
551
|
+
return Array.isArray(list) ? list : [];
|
|
552
|
+
}
|
|
532
553
|
async getPlaylists(auth) {
|
|
533
|
-
const
|
|
534
|
-
const response = await this.apiRequest(apiPath, auth);
|
|
554
|
+
const response = await this.apiRequest("/content/playlists", auth);
|
|
535
555
|
if (!response) return [];
|
|
536
|
-
|
|
537
|
-
if (!Array.isArray(playlists)) return [];
|
|
538
|
-
return playlists.map((p) => ({
|
|
556
|
+
return this.extractList(response, "playlists").map((p) => ({
|
|
539
557
|
type: "folder",
|
|
540
558
|
id: p.id,
|
|
541
559
|
title: p.name,
|
|
@@ -545,26 +563,17 @@ var SignPresenterProvider = class {
|
|
|
545
563
|
}));
|
|
546
564
|
}
|
|
547
565
|
async getMessages(playlistId, auth) {
|
|
548
|
-
const
|
|
549
|
-
const response = await this.apiRequest(pathFn(playlistId), auth);
|
|
566
|
+
const response = await this.apiRequest(`/content/playlists/${playlistId}/messages`, auth);
|
|
550
567
|
if (!response) return [];
|
|
551
|
-
const messages = Array.isArray(response) ? response : response.data || response.messages || [];
|
|
552
|
-
if (!Array.isArray(messages)) return [];
|
|
553
568
|
const files = [];
|
|
554
|
-
for (const msg of messages) {
|
|
569
|
+
for (const msg of this.extractList(response, "messages")) {
|
|
555
570
|
if (!msg.url) continue;
|
|
556
571
|
const url = msg.url;
|
|
557
|
-
const
|
|
558
|
-
|
|
559
|
-
msg.
|
|
560
|
-
msg.
|
|
561
|
-
|
|
562
|
-
{
|
|
563
|
-
mediaType: detectMediaType(url, msg.mediaType),
|
|
564
|
-
thumbnail: msg.thumbnail || msg.image,
|
|
565
|
-
seconds
|
|
566
|
-
}
|
|
567
|
-
);
|
|
572
|
+
const file = createFile(msg.id, msg.name, url, {
|
|
573
|
+
mediaType: detectMediaType(url, msg.mediaType),
|
|
574
|
+
thumbnail: msg.thumbnail || msg.image,
|
|
575
|
+
seconds: msg.seconds
|
|
576
|
+
});
|
|
568
577
|
file.downloadUrl = url;
|
|
569
578
|
files.push(file);
|
|
570
579
|
}
|
|
@@ -573,8 +582,7 @@ var SignPresenterProvider = class {
|
|
|
573
582
|
async getPlaylist(path, auth, _resolution) {
|
|
574
583
|
const { segments, depth } = parsePath(path);
|
|
575
584
|
if (depth < 2 || segments[0] !== "playlists") return null;
|
|
576
|
-
const
|
|
577
|
-
const files = await this.getMessages(playlistId, auth);
|
|
585
|
+
const files = await this.getMessages(segments[1], auth);
|
|
578
586
|
return files.length > 0 ? files : null;
|
|
579
587
|
}
|
|
580
588
|
async getInstructions(path, auth) {
|
|
@@ -584,43 +592,11 @@ var SignPresenterProvider = class {
|
|
|
584
592
|
const files = await this.getMessages(playlistId, auth);
|
|
585
593
|
if (files.length === 0) return null;
|
|
586
594
|
const playlists = await this.getPlaylists(auth);
|
|
587
|
-
const
|
|
588
|
-
const
|
|
589
|
-
|
|
590
|
-
id: file.id + "-action",
|
|
591
|
-
itemType: "action",
|
|
592
|
-
label: file.title,
|
|
593
|
-
actionType: "play",
|
|
594
|
-
seconds: file.seconds,
|
|
595
|
-
children: [
|
|
596
|
-
{
|
|
597
|
-
id: file.id,
|
|
598
|
-
itemType: "file",
|
|
599
|
-
label: file.title,
|
|
600
|
-
seconds: file.seconds,
|
|
601
|
-
downloadUrl: file.downloadUrl || file.url,
|
|
602
|
-
thumbnail: file.thumbnail
|
|
603
|
-
}
|
|
604
|
-
]
|
|
605
|
-
}));
|
|
606
|
-
const sectionItem = {
|
|
607
|
-
id: playlistId + "-section",
|
|
608
|
-
itemType: "section",
|
|
609
|
-
label: title,
|
|
610
|
-
children: actionItems
|
|
611
|
-
};
|
|
612
|
-
const headerItem = {
|
|
613
|
-
id: playlistId + "-header",
|
|
614
|
-
itemType: "header",
|
|
615
|
-
label: title,
|
|
616
|
-
children: [sectionItem]
|
|
617
|
-
};
|
|
618
|
-
return { name: title, items: [headerItem] };
|
|
595
|
+
const title = playlists.find((p) => p.id === playlistId)?.title || "Playlist";
|
|
596
|
+
const instructions = filesToInstructions(title, files, { id: playlistId + "-section", label: title });
|
|
597
|
+
return { name: title, items: [{ id: playlistId + "-header", itemType: "header", label: title, children: instructions.items }] };
|
|
619
598
|
}
|
|
620
599
|
// Auth methods
|
|
621
|
-
supportsDeviceFlow() {
|
|
622
|
-
return this.deviceFlowHelper.supportsDeviceFlow(this.config);
|
|
623
|
-
}
|
|
624
600
|
generateCodeVerifier() {
|
|
625
601
|
return this.oauthHelper.generateCodeVerifier();
|
|
626
602
|
}
|
|
@@ -667,6 +643,23 @@ function truncateForLabel(text) {
|
|
|
667
643
|
const truncated = text.length > cutoff ? text.substring(0, cutoff) : text;
|
|
668
644
|
return stripMarkdown(truncated) + (text.length > cutoff ? "..." : "");
|
|
669
645
|
}
|
|
646
|
+
function getEmbedUrl(apiType, relatedId) {
|
|
647
|
+
if (!relatedId) return void 0;
|
|
648
|
+
const baseUrl = "https://lessons.church";
|
|
649
|
+
switch (apiType) {
|
|
650
|
+
case "action":
|
|
651
|
+
case "lessonAction":
|
|
652
|
+
return `${baseUrl}/embed/action/${relatedId}`;
|
|
653
|
+
case "addon":
|
|
654
|
+
case "lessonAddOn":
|
|
655
|
+
return `${baseUrl}/embed/addon/${relatedId}`;
|
|
656
|
+
case "section":
|
|
657
|
+
case "lessonSection":
|
|
658
|
+
return `${baseUrl}/embed/section/${relatedId}`;
|
|
659
|
+
default:
|
|
660
|
+
return void 0;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
670
663
|
async function convertAddOnToFile(addOn) {
|
|
671
664
|
const apiPath = `/addOns/public/${addOn.id}`;
|
|
672
665
|
const detail = await apiRequest(apiPath);
|
|
@@ -813,12 +806,13 @@ async function convertAddOnCategoryToInstructions(category) {
|
|
|
813
806
|
}
|
|
814
807
|
|
|
815
808
|
// src/providers/lessonsChurch/LessonsChurchProvider.ts
|
|
816
|
-
var LessonsChurchProvider = class {
|
|
809
|
+
var LessonsChurchProvider = class extends BaseProvider {
|
|
817
810
|
constructor() {
|
|
811
|
+
super(...arguments);
|
|
818
812
|
this.id = "lessonschurch";
|
|
819
813
|
this.name = "Lessons.church";
|
|
820
814
|
this.logos = { light: "https://lessons.church/images/logo.png", dark: "https://lessons.church/images/logo-dark.png" };
|
|
821
|
-
this.config = { id: "lessonschurch", name: "Lessons.church", apiBase: API_BASE2, oauthBase: "", clientId: "", scopes: []
|
|
815
|
+
this.config = { id: "lessonschurch", name: "Lessons.church", apiBase: API_BASE2, oauthBase: "", clientId: "", scopes: [] };
|
|
822
816
|
this.requiresAuth = false;
|
|
823
817
|
this.authTypes = ["none"];
|
|
824
818
|
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
@@ -874,28 +868,25 @@ var LessonsChurchProvider = class {
|
|
|
874
868
|
return [];
|
|
875
869
|
}
|
|
876
870
|
async getPrograms() {
|
|
877
|
-
const response = await apiRequest(
|
|
871
|
+
const response = await apiRequest("/programs/public");
|
|
878
872
|
if (!response) return [];
|
|
879
873
|
const programs = Array.isArray(response) ? response : [];
|
|
880
874
|
return programs.map((p) => createFolder(p.id, p.name, `/lessons/${p.id}`, p.image));
|
|
881
875
|
}
|
|
882
876
|
async getStudies(programId, currentPath) {
|
|
883
|
-
const
|
|
884
|
-
const response = await apiRequest(pathFn(programId));
|
|
877
|
+
const response = await apiRequest(`/studies/public/program/${programId}`);
|
|
885
878
|
if (!response) return [];
|
|
886
879
|
const studies = Array.isArray(response) ? response : [];
|
|
887
880
|
return studies.map((s) => createFolder(s.id, s.name, `${currentPath}/${s.id}`, s.image)).sort((a, b) => a.title.localeCompare(b.title));
|
|
888
881
|
}
|
|
889
882
|
async getLessons(studyId, currentPath) {
|
|
890
|
-
const
|
|
891
|
-
const response = await apiRequest(pathFn(studyId));
|
|
883
|
+
const response = await apiRequest(`/lessons/public/study/${studyId}`);
|
|
892
884
|
if (!response) return [];
|
|
893
885
|
const lessons = Array.isArray(response) ? response : [];
|
|
894
886
|
return lessons.map((l) => createFolder(l.id, l.name || l.title, `${currentPath}/${l.id}`, l.image));
|
|
895
887
|
}
|
|
896
888
|
async getVenues(lessonId, currentPath) {
|
|
897
|
-
const
|
|
898
|
-
const response = await apiRequest(pathFn(lessonId));
|
|
889
|
+
const response = await apiRequest(`/venues/public/lesson/${lessonId}`);
|
|
899
890
|
if (!response) return [];
|
|
900
891
|
const lessonResponse = await apiRequest(`/lessons/public/${lessonId}`);
|
|
901
892
|
const lessonImage = lessonResponse?.image;
|
|
@@ -918,7 +909,7 @@ var LessonsChurchProvider = class {
|
|
|
918
909
|
return file ? [file] : [];
|
|
919
910
|
}
|
|
920
911
|
async getAddOnCategories() {
|
|
921
|
-
const response = await apiRequest(
|
|
912
|
+
const response = await apiRequest("/addOns/public");
|
|
922
913
|
if (!response) return [];
|
|
923
914
|
const addOns = Array.isArray(response) ? response : [];
|
|
924
915
|
const categories = Array.from(new Set(addOns.map((a) => a.category).filter(Boolean)));
|
|
@@ -926,7 +917,7 @@ var LessonsChurchProvider = class {
|
|
|
926
917
|
}
|
|
927
918
|
async getAddOnsByCategory(category, currentPath) {
|
|
928
919
|
const decodedCategory = decodeURIComponent(category);
|
|
929
|
-
const response = await apiRequest(
|
|
920
|
+
const response = await apiRequest("/addOns/public");
|
|
930
921
|
if (!response) return [];
|
|
931
922
|
const allAddOns = Array.isArray(response) ? response : [];
|
|
932
923
|
const filtered = allAddOns.filter((a) => a.category === decodedCategory);
|
|
@@ -969,9 +960,6 @@ var LessonsChurchProvider = class {
|
|
|
969
960
|
}
|
|
970
961
|
return null;
|
|
971
962
|
}
|
|
972
|
-
supportsDeviceFlow() {
|
|
973
|
-
return false;
|
|
974
|
-
}
|
|
975
963
|
};
|
|
976
964
|
|
|
977
965
|
// src/providers/registry.ts
|
|
@@ -987,18 +975,6 @@ function registerProvider(provider) {
|
|
|
987
975
|
}
|
|
988
976
|
|
|
989
977
|
// src/providers/b1Church/B1ChurchAuth.ts
|
|
990
|
-
async function generateCodeChallenge(verifier) {
|
|
991
|
-
const encoder = new TextEncoder();
|
|
992
|
-
const data = encoder.encode(verifier);
|
|
993
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
994
|
-
const hashArray = new Uint8Array(hashBuffer);
|
|
995
|
-
let binary = "";
|
|
996
|
-
for (let i = 0; i < hashArray.length; i++) {
|
|
997
|
-
binary += String.fromCharCode(hashArray[i]);
|
|
998
|
-
}
|
|
999
|
-
const base64 = btoa(binary);
|
|
1000
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1001
|
-
}
|
|
1002
978
|
async function buildB1AuthUrl(config, appBase, redirectUri, codeVerifier, state) {
|
|
1003
979
|
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
1004
980
|
const oauthParams = new URLSearchParams({
|
|
@@ -1022,7 +998,7 @@ async function exchangeCodeForTokensWithPKCE(config, code, redirectUri, codeVeri
|
|
|
1022
998
|
return null;
|
|
1023
999
|
}
|
|
1024
1000
|
const data = await response.json();
|
|
1025
|
-
return
|
|
1001
|
+
return toAuthData(data, { scope: config.scopes.join(" ") });
|
|
1026
1002
|
} catch {
|
|
1027
1003
|
return null;
|
|
1028
1004
|
}
|
|
@@ -1036,7 +1012,7 @@ async function exchangeCodeForTokensWithSecret(config, code, redirectUri, client
|
|
|
1036
1012
|
return null;
|
|
1037
1013
|
}
|
|
1038
1014
|
const data = await response.json();
|
|
1039
|
-
return
|
|
1015
|
+
return toAuthData(data, { scope: config.scopes.join(" ") });
|
|
1040
1016
|
} catch {
|
|
1041
1017
|
return null;
|
|
1042
1018
|
}
|
|
@@ -1048,7 +1024,7 @@ async function refreshTokenWithSecret(config, auth, clientSecret) {
|
|
|
1048
1024
|
const response = await fetch(`${config.oauthBase}/token`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(params) });
|
|
1049
1025
|
if (!response.ok) return null;
|
|
1050
1026
|
const data = await response.json();
|
|
1051
|
-
return
|
|
1027
|
+
return toAuthData(data, { refreshToken: auth.refresh_token, scope: auth.scope });
|
|
1052
1028
|
} catch {
|
|
1053
1029
|
return null;
|
|
1054
1030
|
}
|
|
@@ -1073,78 +1049,32 @@ async function authFetch(url, auth) {
|
|
|
1073
1049
|
}
|
|
1074
1050
|
}
|
|
1075
1051
|
async function fetchMinistries(auth) {
|
|
1076
|
-
|
|
1077
|
-
return result || [];
|
|
1052
|
+
return await authFetch(`${API_BASE3}/membership/groups/tag/ministry`, auth) || [];
|
|
1078
1053
|
}
|
|
1079
1054
|
async function fetchPlanTypes(ministryId, auth) {
|
|
1080
|
-
|
|
1081
|
-
return result || [];
|
|
1055
|
+
return await authFetch(`${API_BASE3}/doing/planTypes/ministryId/${ministryId}`, auth) || [];
|
|
1082
1056
|
}
|
|
1083
1057
|
async function fetchPlans(planTypeId, auth) {
|
|
1084
|
-
|
|
1085
|
-
return result || [];
|
|
1058
|
+
return await authFetch(`${API_BASE3}/doing/plans/types/${planTypeId}`, auth) || [];
|
|
1086
1059
|
}
|
|
1087
|
-
var DOING_API_BASE = "https://api.churchapps.org/doing";
|
|
1088
1060
|
async function fetchCurrentPlanByType(planTypeId) {
|
|
1089
|
-
|
|
1090
|
-
const response = await fetch(`${DOING_API_BASE}/plans/public/current/${planTypeId}`, { method: "GET", headers: { Accept: "application/json" } });
|
|
1091
|
-
if (!response.ok) {
|
|
1092
|
-
console.warn(`[B1Church] fetchCurrentPlanByType failed: HTTP ${response.status} for planTypeId=${planTypeId}`);
|
|
1093
|
-
return null;
|
|
1094
|
-
}
|
|
1095
|
-
return await response.json();
|
|
1096
|
-
} catch (err) {
|
|
1097
|
-
console.error(`[B1Church] fetchCurrentPlanByType error: planTypeId=${planTypeId}`, err);
|
|
1098
|
-
return null;
|
|
1099
|
-
}
|
|
1061
|
+
return authFetch(`${API_BASE3}/doing/plans/public/current/${planTypeId}`);
|
|
1100
1062
|
}
|
|
1101
1063
|
async function fetchPlanItems(churchId, planId) {
|
|
1102
|
-
|
|
1103
|
-
const response = await fetch(`${DOING_API_BASE}/planItems/presenter/${churchId}/${planId}`, { method: "GET", headers: { Accept: "application/json" } });
|
|
1104
|
-
if (!response.ok) return [];
|
|
1105
|
-
return await response.json() || [];
|
|
1106
|
-
} catch {
|
|
1107
|
-
return [];
|
|
1108
|
-
}
|
|
1064
|
+
return await authFetch(`${API_BASE3}/doing/planItems/presenter/${churchId}/${planId}`) || [];
|
|
1109
1065
|
}
|
|
1110
1066
|
async function fetchVenueFeed(venueId) {
|
|
1111
|
-
|
|
1112
|
-
const response = await fetch(`${LESSONS_API_BASE}/venues/public/feed/${venueId}`, { method: "GET", headers: { Accept: "application/json" } });
|
|
1113
|
-
if (!response.ok) {
|
|
1114
|
-
console.warn(`[B1Church] fetchVenueFeed failed: HTTP ${response.status} for venueId=${venueId}`);
|
|
1115
|
-
return null;
|
|
1116
|
-
}
|
|
1117
|
-
return await response.json();
|
|
1118
|
-
} catch (err) {
|
|
1119
|
-
console.error(`[B1Church] fetchVenueFeed error: venueId=${venueId}`, err);
|
|
1120
|
-
return null;
|
|
1121
|
-
}
|
|
1067
|
+
return authFetch(`${LESSONS_API_BASE}/venues/public/feed/${venueId}`);
|
|
1122
1068
|
}
|
|
1123
1069
|
async function fetchVenueActions(venueId) {
|
|
1124
|
-
|
|
1125
|
-
const response = await fetch(`${LESSONS_API_BASE}/venues/public/actions/${venueId}`, { method: "GET", headers: { Accept: "application/json" } });
|
|
1126
|
-
if (!response.ok) {
|
|
1127
|
-
console.warn(`[B1Church] fetchVenueActions failed: HTTP ${response.status} for venueId=${venueId}`);
|
|
1128
|
-
return null;
|
|
1129
|
-
}
|
|
1130
|
-
return await response.json();
|
|
1131
|
-
} catch (err) {
|
|
1132
|
-
console.error(`[B1Church] fetchVenueActions error: venueId=${venueId}`, err);
|
|
1133
|
-
return null;
|
|
1134
|
-
}
|
|
1070
|
+
return authFetch(`${LESSONS_API_BASE}/venues/public/actions/${venueId}`);
|
|
1135
1071
|
}
|
|
1136
1072
|
async function fetchVenueImages(venueIds) {
|
|
1137
1073
|
const map = /* @__PURE__ */ new Map();
|
|
1138
1074
|
if (venueIds.length === 0) return map;
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
if (!response.ok) return map;
|
|
1143
|
-
const data = await response.json();
|
|
1144
|
-
for (const row of data) {
|
|
1145
|
-
if (row.venueId && row.lessonImage) map.set(row.venueId, row.lessonImage);
|
|
1146
|
-
}
|
|
1147
|
-
} catch {
|
|
1075
|
+
const data = await authFetch(`${LESSONS_API_BASE}/venues/public/images?ids=${venueIds.join(",")}`);
|
|
1076
|
+
for (const row of data || []) {
|
|
1077
|
+
if (row.venueId && row.lessonImage) map.set(row.venueId, row.lessonImage);
|
|
1148
1078
|
}
|
|
1149
1079
|
return map;
|
|
1150
1080
|
}
|
|
@@ -1233,18 +1163,7 @@ function getFileFromProviderFileItem(item) {
|
|
|
1233
1163
|
};
|
|
1234
1164
|
}
|
|
1235
1165
|
function planItemToInstruction(item, thumbnail) {
|
|
1236
|
-
|
|
1237
|
-
switch (item.itemType) {
|
|
1238
|
-
case "lessonSection":
|
|
1239
|
-
itemType = "section";
|
|
1240
|
-
break;
|
|
1241
|
-
case "lessonAction":
|
|
1242
|
-
itemType = "action";
|
|
1243
|
-
break;
|
|
1244
|
-
case "lessonAddOn":
|
|
1245
|
-
itemType = "action";
|
|
1246
|
-
break;
|
|
1247
|
-
}
|
|
1166
|
+
const itemType = normalizeItemType(item.itemType);
|
|
1248
1167
|
const isFileType = itemType === "file" || item.link && !item.children?.length;
|
|
1249
1168
|
return {
|
|
1250
1169
|
id: item.id,
|
|
@@ -1258,23 +1177,6 @@ function planItemToInstruction(item, thumbnail) {
|
|
|
1258
1177
|
children: item.children?.map((child) => planItemToInstruction(child, thumbnail))
|
|
1259
1178
|
};
|
|
1260
1179
|
}
|
|
1261
|
-
function getEmbedUrl(apiType, relatedId) {
|
|
1262
|
-
if (!relatedId) return void 0;
|
|
1263
|
-
const baseUrl = "https://lessons.church";
|
|
1264
|
-
switch (apiType) {
|
|
1265
|
-
case "action":
|
|
1266
|
-
case "lessonAction":
|
|
1267
|
-
return `${baseUrl}/embed/action/${relatedId}`;
|
|
1268
|
-
case "addon":
|
|
1269
|
-
case "lessonAddOn":
|
|
1270
|
-
return `${baseUrl}/embed/addon/${relatedId}`;
|
|
1271
|
-
case "section":
|
|
1272
|
-
case "lessonSection":
|
|
1273
|
-
return `${baseUrl}/embed/section/${relatedId}`;
|
|
1274
|
-
default:
|
|
1275
|
-
return void 0;
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
1180
|
function buildSectionActionsMap2(actionsResponse, thumbnail) {
|
|
1279
1181
|
const sectionActionsMap = /* @__PURE__ */ new Map();
|
|
1280
1182
|
if (actionsResponse?.sections) {
|
|
@@ -1388,9 +1290,9 @@ function isExternalProviderItem(item) {
|
|
|
1388
1290
|
const itemType = item.itemType || "";
|
|
1389
1291
|
return itemType.startsWith("provider");
|
|
1390
1292
|
}
|
|
1391
|
-
var B1ChurchProvider = class {
|
|
1293
|
+
var B1ChurchProvider = class extends BaseProvider {
|
|
1392
1294
|
constructor() {
|
|
1393
|
-
|
|
1295
|
+
super(...arguments);
|
|
1394
1296
|
this.deviceFlowHelper = new DeviceFlowHelper();
|
|
1395
1297
|
// Unified cache for external provider data to avoid duplicate calls across methods
|
|
1396
1298
|
this.externalContentCache = {
|
|
@@ -1401,16 +1303,13 @@ var B1ChurchProvider = class {
|
|
|
1401
1303
|
this.id = "b1church";
|
|
1402
1304
|
this.name = "B1.Church";
|
|
1403
1305
|
this.logos = { light: "https://b1.church/b1-church-logo.png", dark: "https://b1.church/b1-church-logo.png" };
|
|
1404
|
-
this.config = { id: "b1church", name: "B1.Church", apiBase: `${API_BASE3}/doing`, oauthBase: `${API_BASE3}/membership/oauth`, clientId: "nsowldn58dk", scopes: ["plans"], supportsDeviceFlow: true, deviceAuthEndpoint: "/device/authorize"
|
|
1306
|
+
this.config = { id: "b1church", name: "B1.Church", apiBase: `${API_BASE3}/doing`, oauthBase: `${API_BASE3}/membership/oauth`, clientId: "nsowldn58dk", scopes: ["plans"], supportsDeviceFlow: true, deviceAuthEndpoint: "/device/authorize" };
|
|
1405
1307
|
this.appBase = "https://admin.b1.church";
|
|
1406
1308
|
this.requiresAuth = true;
|
|
1407
1309
|
this.authTypes = ["oauth_pkce", "device_flow"];
|
|
1408
1310
|
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
1409
1311
|
this.planTypeId = null;
|
|
1410
1312
|
}
|
|
1411
|
-
async apiRequest(path, authData) {
|
|
1412
|
-
return this.apiHelper.apiRequest(this.config, this.id, path, authData);
|
|
1413
|
-
}
|
|
1414
1313
|
async buildAuthUrl(codeVerifier, redirectUri, state) {
|
|
1415
1314
|
return buildB1AuthUrl(this.config, this.appBase, redirectUri, codeVerifier, state);
|
|
1416
1315
|
}
|
|
@@ -1556,8 +1455,7 @@ var B1ChurchProvider = class {
|
|
|
1556
1455
|
console.warn("[B1Church getInstructions] planFolder missing churchId:", planFolder.id);
|
|
1557
1456
|
return null;
|
|
1558
1457
|
}
|
|
1559
|
-
const
|
|
1560
|
-
const planItems = await this.apiRequest(pathFn(churchId, planId), authData);
|
|
1458
|
+
const planItems = await this.apiRequest(`/planFeed/presenter/${churchId}/${planId}`, authData);
|
|
1561
1459
|
if ((!planItems || planItems.length === 0) && planFolder.providerId && planFolder.providerPlanId) {
|
|
1562
1460
|
const externalInstructions = await fetchFromProviderProxy(
|
|
1563
1461
|
"getInstructions",
|
|
@@ -1665,8 +1563,7 @@ var B1ChurchProvider = class {
|
|
|
1665
1563
|
console.warn("[B1Church] getPlaylist: planFolder missing churchId:", planFolder.id);
|
|
1666
1564
|
return null;
|
|
1667
1565
|
}
|
|
1668
|
-
const
|
|
1669
|
-
const planItems = await this.apiRequest(pathFn(churchId, planId), authData);
|
|
1566
|
+
const planItems = await this.apiRequest(`/planFeed/presenter/${churchId}/${planId}`, authData);
|
|
1670
1567
|
if ((!planItems || planItems.length === 0) && planFolder.providerId && planFolder.providerPlanId) {
|
|
1671
1568
|
const externalFiles = await fetchFromProviderProxy(
|
|
1672
1569
|
"getPlaylist",
|
|
@@ -1761,9 +1658,6 @@ var B1ChurchProvider = class {
|
|
|
1761
1658
|
}
|
|
1762
1659
|
return files;
|
|
1763
1660
|
}
|
|
1764
|
-
supportsDeviceFlow() {
|
|
1765
|
-
return !!this.config.supportsDeviceFlow;
|
|
1766
|
-
}
|
|
1767
1661
|
};
|
|
1768
1662
|
|
|
1769
1663
|
// src/providers/dropbox/DropboxConverters.ts
|
|
@@ -1789,38 +1683,13 @@ function filterMediaEntries(entries) {
|
|
|
1789
1683
|
}
|
|
1790
1684
|
return { folders, mediaFiles };
|
|
1791
1685
|
}
|
|
1792
|
-
function buildInstructionsFromFiles(files, folderName) {
|
|
1793
|
-
const actionItems = files.map((file) => ({
|
|
1794
|
-
id: file.id + "-action",
|
|
1795
|
-
itemType: "action",
|
|
1796
|
-
label: file.title,
|
|
1797
|
-
actionType: "play",
|
|
1798
|
-
children: [
|
|
1799
|
-
{
|
|
1800
|
-
id: file.id,
|
|
1801
|
-
itemType: "file",
|
|
1802
|
-
label: file.title,
|
|
1803
|
-
seconds: file.seconds,
|
|
1804
|
-
downloadUrl: file.downloadUrl || file.url,
|
|
1805
|
-
thumbnail: file.thumbnail
|
|
1806
|
-
}
|
|
1807
|
-
]
|
|
1808
|
-
}));
|
|
1809
|
-
const sectionItem = {
|
|
1810
|
-
id: "dropbox-section",
|
|
1811
|
-
itemType: "section",
|
|
1812
|
-
label: folderName,
|
|
1813
|
-
children: actionItems
|
|
1814
|
-
};
|
|
1815
|
-
return { name: folderName, items: [sectionItem] };
|
|
1816
|
-
}
|
|
1817
1686
|
|
|
1818
1687
|
// src/providers/dropbox/DropboxProvider.ts
|
|
1819
1688
|
var MAX_PAGINATION_CALLS = 50;
|
|
1820
|
-
var DropboxProvider = class {
|
|
1689
|
+
var DropboxProvider = class extends BaseProvider {
|
|
1821
1690
|
constructor() {
|
|
1691
|
+
super(...arguments);
|
|
1822
1692
|
this.oauthHelper = new OAuthHelper();
|
|
1823
|
-
this.apiHelper = new ApiHelper();
|
|
1824
1693
|
this.id = "dropbox";
|
|
1825
1694
|
this.name = "Dropbox";
|
|
1826
1695
|
this.logos = {
|
|
@@ -1833,12 +1702,7 @@ var DropboxProvider = class {
|
|
|
1833
1702
|
apiBase: "https://api.dropboxapi.com",
|
|
1834
1703
|
oauthBase: "https://www.dropbox.com/oauth2",
|
|
1835
1704
|
clientId: "9io0q0q9angdz9j",
|
|
1836
|
-
scopes: []
|
|
1837
|
-
endpoints: {
|
|
1838
|
-
listFolder: "/2/files/list_folder",
|
|
1839
|
-
listFolderContinue: "/2/files/list_folder/continue",
|
|
1840
|
-
getTemporaryLink: "/2/files/get_temporary_link"
|
|
1841
|
-
}
|
|
1705
|
+
scopes: []
|
|
1842
1706
|
};
|
|
1843
1707
|
this.requiresAuth = true;
|
|
1844
1708
|
this.authTypes = ["oauth_pkce"];
|
|
@@ -1855,12 +1719,12 @@ var DropboxProvider = class {
|
|
|
1855
1719
|
console.error("[Dropbox] No auth token provided");
|
|
1856
1720
|
return Promise.resolve(null);
|
|
1857
1721
|
}
|
|
1858
|
-
return this.
|
|
1722
|
+
return this.apiRequest(endpoint, auth, "POST", body);
|
|
1859
1723
|
}
|
|
1860
1724
|
async listAllEntries(dropboxPath, auth) {
|
|
1861
1725
|
const allEntries = [];
|
|
1862
1726
|
const response = await this.dropboxPost(
|
|
1863
|
-
|
|
1727
|
+
"/2/files/list_folder",
|
|
1864
1728
|
{ path: dropboxPath, recursive: false, include_media_info: false },
|
|
1865
1729
|
auth
|
|
1866
1730
|
);
|
|
@@ -1871,7 +1735,7 @@ var DropboxProvider = class {
|
|
|
1871
1735
|
let calls = 0;
|
|
1872
1736
|
while (hasMore && calls < MAX_PAGINATION_CALLS) {
|
|
1873
1737
|
const cont = await this.dropboxPost(
|
|
1874
|
-
|
|
1738
|
+
"/2/files/list_folder/continue",
|
|
1875
1739
|
{ cursor },
|
|
1876
1740
|
auth
|
|
1877
1741
|
);
|
|
@@ -1921,7 +1785,7 @@ var DropboxProvider = class {
|
|
|
1921
1785
|
}
|
|
1922
1786
|
async getTemporaryLink(filePath, auth) {
|
|
1923
1787
|
const response = await this.dropboxPost(
|
|
1924
|
-
|
|
1788
|
+
"/2/files/get_temporary_link",
|
|
1925
1789
|
{ path: filePath },
|
|
1926
1790
|
auth
|
|
1927
1791
|
);
|
|
@@ -1942,7 +1806,7 @@ var DropboxProvider = class {
|
|
|
1942
1806
|
// -- Leaf detection --
|
|
1943
1807
|
async checkIsLeaf(folderPath, auth) {
|
|
1944
1808
|
const response = await this.dropboxPost(
|
|
1945
|
-
|
|
1809
|
+
"/2/files/list_folder",
|
|
1946
1810
|
{ path: folderPath, recursive: false, include_media_info: false },
|
|
1947
1811
|
auth
|
|
1948
1812
|
);
|
|
@@ -1961,31 +1825,29 @@ var DropboxProvider = class {
|
|
|
1961
1825
|
const fileItems = await this.resolveFileLinks(mediaFiles, auth);
|
|
1962
1826
|
return [...folderItems, ...fileItems];
|
|
1963
1827
|
}
|
|
1964
|
-
|
|
1828
|
+
/** Resolve a folder path to playable files (list + media filter + link resolution). */
|
|
1829
|
+
async resolveMediaFiles(path, auth) {
|
|
1965
1830
|
if (!path || path === "/") return null;
|
|
1966
1831
|
const dropboxPath = path.startsWith("/") ? path : "/" + path;
|
|
1967
1832
|
const allEntries = await this.listAllEntries(dropboxPath, auth);
|
|
1968
1833
|
const { mediaFiles } = filterMediaEntries(allEntries);
|
|
1969
1834
|
if (mediaFiles.length === 0) return null;
|
|
1970
1835
|
const files = await this.resolveFileLinks(mediaFiles, auth);
|
|
1971
|
-
|
|
1836
|
+
if (files.length === 0) return null;
|
|
1837
|
+
return { files, dropboxPath };
|
|
1838
|
+
}
|
|
1839
|
+
async getPlaylist(path, auth, _resolution) {
|
|
1840
|
+
const resolved = await this.resolveMediaFiles(path, auth);
|
|
1841
|
+
return resolved?.files ?? null;
|
|
1972
1842
|
}
|
|
1973
1843
|
async getInstructions(path, auth) {
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
const
|
|
1977
|
-
const { mediaFiles } = filterMediaEntries(allEntries);
|
|
1978
|
-
if (mediaFiles.length === 0) return null;
|
|
1979
|
-
const files = await this.resolveFileLinks(mediaFiles, auth);
|
|
1980
|
-
if (files.length === 0) return null;
|
|
1981
|
-
const segments = dropboxPath.split("/").filter(Boolean);
|
|
1844
|
+
const resolved = await this.resolveMediaFiles(path, auth);
|
|
1845
|
+
if (!resolved) return null;
|
|
1846
|
+
const segments = resolved.dropboxPath.split("/").filter(Boolean);
|
|
1982
1847
|
const folderName = segments[segments.length - 1] || "Dropbox";
|
|
1983
|
-
return
|
|
1848
|
+
return filesToInstructions(folderName, resolved.files, { id: "dropbox-section", label: folderName });
|
|
1984
1849
|
}
|
|
1985
1850
|
// -- Auth methods --
|
|
1986
|
-
supportsDeviceFlow() {
|
|
1987
|
-
return false;
|
|
1988
|
-
}
|
|
1989
1851
|
generateCodeVerifier() {
|
|
1990
1852
|
return this.oauthHelper.generateCodeVerifier();
|
|
1991
1853
|
}
|
|
@@ -2022,61 +1884,43 @@ var DropboxProvider = class {
|
|
|
2022
1884
|
body: params.toString()
|
|
2023
1885
|
});
|
|
2024
1886
|
if (!response.ok) return null;
|
|
2025
|
-
|
|
2026
|
-
return {
|
|
2027
|
-
access_token: data.access_token,
|
|
2028
|
-
refresh_token: data.refresh_token,
|
|
2029
|
-
token_type: data.token_type || "Bearer",
|
|
2030
|
-
created_at: Math.floor(Date.now() / 1e3),
|
|
2031
|
-
expires_in: data.expires_in,
|
|
2032
|
-
scope: data.scope || ""
|
|
2033
|
-
};
|
|
1887
|
+
return toAuthData(await response.json());
|
|
2034
1888
|
} catch {
|
|
2035
1889
|
return null;
|
|
2036
1890
|
}
|
|
2037
1891
|
}
|
|
2038
1892
|
};
|
|
2039
1893
|
|
|
2040
|
-
// src/providers/planningCenter/
|
|
2041
|
-
var
|
|
1894
|
+
// src/providers/planningCenter/PlanningCenterProvider.ts
|
|
1895
|
+
var ONE_WEEK_MS = 6048e5;
|
|
2042
1896
|
function formatDate(dateString) {
|
|
2043
|
-
|
|
2044
|
-
return date.toISOString().slice(0, 10);
|
|
1897
|
+
return new Date(dateString).toISOString().slice(0, 10);
|
|
2045
1898
|
}
|
|
2046
|
-
|
|
2047
|
-
// src/providers/planningCenter/PlanningCenterProvider.ts
|
|
2048
|
-
var PlanningCenterProvider = class {
|
|
1899
|
+
var PlanningCenterProvider = class extends BaseProvider {
|
|
2049
1900
|
constructor() {
|
|
2050
|
-
|
|
1901
|
+
super(...arguments);
|
|
2051
1902
|
this.id = "planningcenter";
|
|
2052
1903
|
this.name = "Planning Center";
|
|
2053
1904
|
this.logos = { light: "https://www.planningcenter.com/icons/icon-512x512.png", dark: "https://www.planningcenter.com/icons/icon-512x512.png" };
|
|
2054
|
-
this.config = { id: "planningcenter", name: "Planning Center", apiBase: "https://api.planningcenteronline.com", oauthBase: "https://api.planningcenteronline.com/oauth", clientId: "", scopes: ["services"]
|
|
2055
|
-
this.ONE_WEEK_MS = 6048e5;
|
|
1905
|
+
this.config = { id: "planningcenter", name: "Planning Center", apiBase: "https://api.planningcenteronline.com", oauthBase: "https://api.planningcenteronline.com/oauth", clientId: "", scopes: ["services"] };
|
|
2056
1906
|
this.requiresAuth = true;
|
|
2057
1907
|
this.authTypes = ["oauth_pkce"];
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
async apiRequest(path, auth) {
|
|
2061
|
-
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
1908
|
+
// Plan items come back without media URLs, so playlist/instructions are not actually supported yet.
|
|
1909
|
+
this.capabilities = { browse: true, playlist: false, instructions: false, mediaLicensing: false };
|
|
2062
1910
|
}
|
|
2063
1911
|
async browse(path, auth) {
|
|
2064
1912
|
const { segments, depth } = parsePath(path);
|
|
2065
1913
|
if (depth === 0) {
|
|
2066
1914
|
return [{ type: "folder", id: "serviceTypes-root", title: "Service Types", path: "/serviceTypes" }];
|
|
2067
1915
|
}
|
|
2068
|
-
|
|
2069
|
-
if (root !== "serviceTypes") return [];
|
|
1916
|
+
if (segments[0] !== "serviceTypes") return [];
|
|
2070
1917
|
if (depth === 1) return this.getServiceTypes(auth);
|
|
2071
1918
|
if (depth === 2) return this.getPlans(segments[1], path, auth);
|
|
2072
1919
|
if (depth === 3) return this.getPlanItems(segments[1], segments[2], auth);
|
|
2073
1920
|
return [];
|
|
2074
1921
|
}
|
|
2075
1922
|
async getServiceTypes(auth) {
|
|
2076
|
-
const response = await this.apiRequest(
|
|
2077
|
-
this.config.endpoints.serviceTypes,
|
|
2078
|
-
auth
|
|
2079
|
-
);
|
|
1923
|
+
const response = await this.apiRequest("/services/v2/service_types", auth);
|
|
2080
1924
|
if (!response?.data) return [];
|
|
2081
1925
|
return response.data.map((serviceType) => ({
|
|
2082
1926
|
type: "folder",
|
|
@@ -2086,9 +1930,8 @@ var PlanningCenterProvider = class {
|
|
|
2086
1930
|
}));
|
|
2087
1931
|
}
|
|
2088
1932
|
async getPlans(serviceTypeId, currentPath, auth) {
|
|
2089
|
-
const pathFn = this.config.endpoints.plans;
|
|
2090
1933
|
const response = await this.apiRequest(
|
|
2091
|
-
|
|
1934
|
+
`/services/v2/service_types/${serviceTypeId}/plans?filter=future&order=sort_date`,
|
|
2092
1935
|
auth
|
|
2093
1936
|
);
|
|
2094
1937
|
if (!response?.data) return [];
|
|
@@ -2096,7 +1939,7 @@ var PlanningCenterProvider = class {
|
|
|
2096
1939
|
const filteredPlans = response.data.filter((plan) => {
|
|
2097
1940
|
if (plan.attributes.items_count === 0) return false;
|
|
2098
1941
|
const planDate = new Date(plan.attributes.sort_date).getTime();
|
|
2099
|
-
return planDate < now +
|
|
1942
|
+
return planDate < now + ONE_WEEK_MS;
|
|
2100
1943
|
});
|
|
2101
1944
|
return filteredPlans.map((plan) => ({
|
|
2102
1945
|
type: "folder",
|
|
@@ -2107,17 +1950,13 @@ var PlanningCenterProvider = class {
|
|
|
2107
1950
|
}));
|
|
2108
1951
|
}
|
|
2109
1952
|
async getPlanItems(serviceTypeId, planId, auth) {
|
|
2110
|
-
const pathFn = this.config.endpoints.planItems;
|
|
2111
1953
|
const response = await this.apiRequest(
|
|
2112
|
-
|
|
1954
|
+
`/services/v2/service_types/${serviceTypeId}/plans/${planId}/items?per_page=100`,
|
|
2113
1955
|
auth
|
|
2114
1956
|
);
|
|
2115
1957
|
if (!response?.data) return [];
|
|
2116
1958
|
return response.data.map((item) => ({ type: "file", id: item.id, title: item.attributes.title || "", mediaType: "image", url: "" }));
|
|
2117
1959
|
}
|
|
2118
|
-
supportsDeviceFlow() {
|
|
2119
|
-
return false;
|
|
2120
|
-
}
|
|
2121
1960
|
};
|
|
2122
1961
|
|
|
2123
1962
|
// src/providers/bibleProject/data.json
|
|
@@ -4512,8 +4351,9 @@ var data_default = {
|
|
|
4512
4351
|
};
|
|
4513
4352
|
|
|
4514
4353
|
// src/providers/bibleProject/BibleProjectProvider.ts
|
|
4515
|
-
var BibleProjectProvider = class {
|
|
4354
|
+
var BibleProjectProvider = class extends BaseProvider {
|
|
4516
4355
|
constructor() {
|
|
4356
|
+
super(...arguments);
|
|
4517
4357
|
this.id = "bibleproject";
|
|
4518
4358
|
this.name = "The Bible Project";
|
|
4519
4359
|
this.logos = {
|
|
@@ -4526,10 +4366,8 @@ var BibleProjectProvider = class {
|
|
|
4526
4366
|
apiBase: "https://bibleproject.com",
|
|
4527
4367
|
oauthBase: "",
|
|
4528
4368
|
clientId: "",
|
|
4529
|
-
scopes: []
|
|
4530
|
-
endpoints: { downloads: "/downloads/" }
|
|
4369
|
+
scopes: []
|
|
4531
4370
|
};
|
|
4532
|
-
this.data = data_default;
|
|
4533
4371
|
this.requiresAuth = false;
|
|
4534
4372
|
this.authTypes = ["none"];
|
|
4535
4373
|
this.capabilities = {
|
|
@@ -4538,133 +4376,71 @@ var BibleProjectProvider = class {
|
|
|
4538
4376
|
instructions: true,
|
|
4539
4377
|
mediaLicensing: false
|
|
4540
4378
|
};
|
|
4379
|
+
this.data = data_default;
|
|
4541
4380
|
}
|
|
4542
4381
|
getMuxThumbnail(muxPlaybackId, width = 400, height = 225) {
|
|
4543
4382
|
return `https://image.mux.com/${muxPlaybackId}/thumbnail.jpg?width=${width}&height=${height}`;
|
|
4544
4383
|
}
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
return this.getCollections();
|
|
4549
|
-
}
|
|
4550
|
-
if (depth === 1) {
|
|
4551
|
-
const collectionSlug = segments[0];
|
|
4552
|
-
return this.getLessonFolders(collectionSlug, path);
|
|
4553
|
-
}
|
|
4554
|
-
if (depth === 2) {
|
|
4555
|
-
const collectionSlug = segments[0];
|
|
4556
|
-
const videoId = segments[1];
|
|
4557
|
-
return this.getVideoFile(collectionSlug, videoId);
|
|
4558
|
-
}
|
|
4559
|
-
return [];
|
|
4560
|
-
}
|
|
4561
|
-
getCollections() {
|
|
4562
|
-
return this.data.collections.filter((collection) => collection.videos.length > 0).map((collection) => ({
|
|
4563
|
-
type: "folder",
|
|
4564
|
-
id: slugify(collection.name),
|
|
4565
|
-
title: collection.name,
|
|
4566
|
-
thumbnail: collection.image || void 0,
|
|
4567
|
-
path: `/${slugify(collection.name)}`
|
|
4568
|
-
}));
|
|
4569
|
-
}
|
|
4570
|
-
getLessonFolders(collectionSlug, currentPath) {
|
|
4571
|
-
const collection = this.data.collections.find((c) => slugify(c.name) === collectionSlug);
|
|
4572
|
-
if (!collection) return [];
|
|
4573
|
-
return collection.videos.map((video) => ({
|
|
4574
|
-
type: "folder",
|
|
4575
|
-
id: video.id,
|
|
4576
|
-
title: video.title,
|
|
4384
|
+
videoToFile(video) {
|
|
4385
|
+
return createFile(video.id, video.title, video.videoUrl, {
|
|
4386
|
+
mediaType: "video",
|
|
4577
4387
|
thumbnail: this.getMuxThumbnail(video.muxPlaybackId),
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
})
|
|
4581
|
-
}
|
|
4582
|
-
getVideoFile(collectionSlug, videoId) {
|
|
4583
|
-
const collection = this.data.collections.find((c) => slugify(c.name) === collectionSlug);
|
|
4584
|
-
if (!collection) return [];
|
|
4585
|
-
const video = collection.videos.find((v) => v.id === videoId);
|
|
4586
|
-
if (!video) return [];
|
|
4587
|
-
return [createFile(video.id, video.title, video.videoUrl, { mediaType: "video", muxPlaybackId: video.muxPlaybackId, seconds: 0 })];
|
|
4388
|
+
muxPlaybackId: video.muxPlaybackId,
|
|
4389
|
+
seconds: 0
|
|
4390
|
+
});
|
|
4588
4391
|
}
|
|
4589
|
-
|
|
4392
|
+
/** Resolve a path to its collection and files: all videos at depth 1, a single video at depth 2. */
|
|
4393
|
+
resolve(path) {
|
|
4590
4394
|
const { segments, depth } = parsePath(path);
|
|
4591
|
-
if (depth < 1) return null;
|
|
4592
|
-
const
|
|
4593
|
-
const collection = this.data.collections.find((c) => slugify(c.name) === collectionSlug);
|
|
4395
|
+
if (depth < 1 || depth > 2) return null;
|
|
4396
|
+
const collection = this.data.collections.find((c) => slugify(c.name) === segments[0]);
|
|
4594
4397
|
if (!collection) return null;
|
|
4595
4398
|
if (depth === 1) {
|
|
4596
|
-
|
|
4597
|
-
return files.length > 0 ? files : null;
|
|
4399
|
+
return { collection, files: collection.videos.map((v) => this.videoToFile(v)), name: collection.name };
|
|
4598
4400
|
}
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
if (!video) return null;
|
|
4603
|
-
return [{ type: "file", id: video.id, title: video.title, mediaType: "video", url: video.videoUrl, thumbnail: this.getMuxThumbnail(video.muxPlaybackId), muxPlaybackId: video.muxPlaybackId, seconds: 0 }];
|
|
4604
|
-
}
|
|
4605
|
-
return null;
|
|
4401
|
+
const video = collection.videos.find((v) => v.id === segments[1]);
|
|
4402
|
+
if (!video) return null;
|
|
4403
|
+
return { collection, files: [this.videoToFile(video)], name: video.title };
|
|
4606
4404
|
}
|
|
4607
|
-
async
|
|
4405
|
+
async browse(path, _auth) {
|
|
4608
4406
|
const { segments, depth } = parsePath(path);
|
|
4609
|
-
if (depth
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4407
|
+
if (depth === 0) {
|
|
4408
|
+
return this.data.collections.filter((collection) => collection.videos.length > 0).map((collection) => ({
|
|
4409
|
+
type: "folder",
|
|
4410
|
+
id: slugify(collection.name),
|
|
4411
|
+
title: collection.name,
|
|
4412
|
+
thumbnail: collection.image || void 0,
|
|
4413
|
+
path: `/${slugify(collection.name)}`
|
|
4414
|
+
}));
|
|
4415
|
+
}
|
|
4613
4416
|
if (depth === 1) {
|
|
4614
|
-
const
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
label: video.title,
|
|
4624
|
-
downloadUrl: video.videoUrl,
|
|
4625
|
-
thumbnail: this.getMuxThumbnail(video.muxPlaybackId)
|
|
4626
|
-
}
|
|
4627
|
-
]
|
|
4417
|
+
const collection = this.data.collections.find((c) => slugify(c.name) === segments[0]);
|
|
4418
|
+
if (!collection) return [];
|
|
4419
|
+
return collection.videos.map((video) => ({
|
|
4420
|
+
type: "folder",
|
|
4421
|
+
id: video.id,
|
|
4422
|
+
title: video.title,
|
|
4423
|
+
thumbnail: this.getMuxThumbnail(video.muxPlaybackId),
|
|
4424
|
+
isLeaf: true,
|
|
4425
|
+
path: `${path}/${video.id}`
|
|
4628
4426
|
}));
|
|
4629
|
-
const sectionItem = {
|
|
4630
|
-
id: slugify(collection.name) + "-section",
|
|
4631
|
-
itemType: "section",
|
|
4632
|
-
label: collection.name,
|
|
4633
|
-
children: actionItems
|
|
4634
|
-
};
|
|
4635
|
-
return { name: collection.name, items: [sectionItem] };
|
|
4636
4427
|
}
|
|
4637
4428
|
if (depth === 2) {
|
|
4638
|
-
|
|
4639
|
-
const video = collection.videos.find((v) => v.id === videoId);
|
|
4640
|
-
if (!video) return null;
|
|
4641
|
-
const actionItem = {
|
|
4642
|
-
id: video.id + "-action",
|
|
4643
|
-
itemType: "action",
|
|
4644
|
-
label: video.title,
|
|
4645
|
-
actionType: "play",
|
|
4646
|
-
children: [
|
|
4647
|
-
{
|
|
4648
|
-
id: video.id,
|
|
4649
|
-
itemType: "file",
|
|
4650
|
-
label: video.title,
|
|
4651
|
-
downloadUrl: video.videoUrl,
|
|
4652
|
-
thumbnail: this.getMuxThumbnail(video.muxPlaybackId)
|
|
4653
|
-
}
|
|
4654
|
-
]
|
|
4655
|
-
};
|
|
4656
|
-
const sectionItem = {
|
|
4657
|
-
id: slugify(collection.name) + "-section",
|
|
4658
|
-
itemType: "section",
|
|
4659
|
-
label: collection.name,
|
|
4660
|
-
children: [actionItem]
|
|
4661
|
-
};
|
|
4662
|
-
return { name: video.title, items: [sectionItem] };
|
|
4429
|
+
return this.resolve(path)?.files ?? [];
|
|
4663
4430
|
}
|
|
4664
|
-
return
|
|
4431
|
+
return [];
|
|
4665
4432
|
}
|
|
4666
|
-
|
|
4667
|
-
|
|
4433
|
+
async getPlaylist(path, _auth, _resolution) {
|
|
4434
|
+
const resolved = this.resolve(path);
|
|
4435
|
+
return resolved && resolved.files.length > 0 ? resolved.files : null;
|
|
4436
|
+
}
|
|
4437
|
+
async getInstructions(path, _auth) {
|
|
4438
|
+
const resolved = this.resolve(path);
|
|
4439
|
+
if (!resolved) return null;
|
|
4440
|
+
return filesToInstructions(resolved.name, resolved.files, {
|
|
4441
|
+
id: slugify(resolved.collection.name) + "-section",
|
|
4442
|
+
label: resolved.collection.name
|
|
4443
|
+
});
|
|
4668
4444
|
}
|
|
4669
4445
|
};
|
|
4670
4446
|
|
|
@@ -12288,8 +12064,9 @@ function buildLessonInstructions(lesson) {
|
|
|
12288
12064
|
}
|
|
12289
12065
|
|
|
12290
12066
|
// src/providers/highVoltage/HighVoltageKidsProvider.ts
|
|
12291
|
-
var HighVoltageKidsProvider = class {
|
|
12067
|
+
var HighVoltageKidsProvider = class extends BaseProvider {
|
|
12292
12068
|
constructor() {
|
|
12069
|
+
super(...arguments);
|
|
12293
12070
|
this.id = "highvoltagekids";
|
|
12294
12071
|
this.name = "High Voltage Kids";
|
|
12295
12072
|
this.logos = {
|
|
@@ -12302,8 +12079,7 @@ var HighVoltageKidsProvider = class {
|
|
|
12302
12079
|
apiBase: "https://highvoltagekids.com",
|
|
12303
12080
|
oauthBase: "",
|
|
12304
12081
|
clientId: "",
|
|
12305
|
-
scopes: []
|
|
12306
|
-
endpoints: { downloads: "/membership-downloads/" }
|
|
12082
|
+
scopes: []
|
|
12307
12083
|
};
|
|
12308
12084
|
this.data = data_default2;
|
|
12309
12085
|
this.requiresAuth = false;
|
|
@@ -12353,9 +12129,6 @@ var HighVoltageKidsProvider = class {
|
|
|
12353
12129
|
}
|
|
12354
12130
|
return null;
|
|
12355
12131
|
}
|
|
12356
|
-
supportsDeviceFlow() {
|
|
12357
|
-
return false;
|
|
12358
|
-
}
|
|
12359
12132
|
};
|
|
12360
12133
|
|
|
12361
12134
|
// src/providers/jesusFilm/JesusFilmProvider.ts
|
|
@@ -12367,8 +12140,14 @@ var CATEGORY_MAP = {
|
|
|
12367
12140
|
"series": "series",
|
|
12368
12141
|
"collections": "collection"
|
|
12369
12142
|
};
|
|
12370
|
-
var
|
|
12143
|
+
var CATEGORY_NAMES = {
|
|
12144
|
+
"feature-films": "Feature Films",
|
|
12145
|
+
"series": "Series",
|
|
12146
|
+
"collections": "Collections"
|
|
12147
|
+
};
|
|
12148
|
+
var JesusFilmProvider = class extends BaseProvider {
|
|
12371
12149
|
constructor() {
|
|
12150
|
+
super(...arguments);
|
|
12372
12151
|
this.id = "jesusfilm";
|
|
12373
12152
|
this.name = "Jesus Film Project";
|
|
12374
12153
|
this.logos = {
|
|
@@ -12381,8 +12160,7 @@ var JesusFilmProvider = class {
|
|
|
12381
12160
|
apiBase: API_BASE4,
|
|
12382
12161
|
oauthBase: "",
|
|
12383
12162
|
clientId: "",
|
|
12384
|
-
scopes: []
|
|
12385
|
-
endpoints: { watch: "/watch" }
|
|
12163
|
+
scopes: []
|
|
12386
12164
|
};
|
|
12387
12165
|
this.requiresAuth = false;
|
|
12388
12166
|
this.authTypes = ["none"];
|
|
@@ -12393,6 +12171,7 @@ var JesusFilmProvider = class {
|
|
|
12393
12171
|
mediaLicensing: false
|
|
12394
12172
|
};
|
|
12395
12173
|
}
|
|
12174
|
+
// Not ApiHelper: Arclight needs the apiKey query param and callers rely on throw-on-error.
|
|
12396
12175
|
async fetchApi(path) {
|
|
12397
12176
|
const separator = path.includes("?") ? "&" : "?";
|
|
12398
12177
|
const url = `${API_BASE4}${path}${separator}apiKey=${API_KEY}`;
|
|
@@ -12400,6 +12179,12 @@ var JesusFilmProvider = class {
|
|
|
12400
12179
|
if (!response.ok) throw new Error(`Arclight API error: ${response.status}`);
|
|
12401
12180
|
return response.json();
|
|
12402
12181
|
}
|
|
12182
|
+
async fetchCategoryComponents(subType) {
|
|
12183
|
+
const data = await this.fetchApi(
|
|
12184
|
+
`/media-components?filter=master&subTypes=${subType}&languageIds=${LANGUAGE_ID}&limit=100`
|
|
12185
|
+
);
|
|
12186
|
+
return data._embedded.mediaComponents;
|
|
12187
|
+
}
|
|
12403
12188
|
async browse(path, _auth) {
|
|
12404
12189
|
const { segments, depth } = parsePath(path);
|
|
12405
12190
|
if (depth === 0) return this.getCategories();
|
|
@@ -12422,19 +12207,14 @@ var JesusFilmProvider = class {
|
|
|
12422
12207
|
async getItemsInCategory(category) {
|
|
12423
12208
|
const subType = CATEGORY_MAP[category];
|
|
12424
12209
|
if (!subType) return [];
|
|
12425
|
-
const
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
`/${category}/${item.mediaComponentId}`,
|
|
12434
|
-
item.imageUrls.mobileCinematicHigh || item.imageUrls.videoStill || item.imageUrls.thumbnail,
|
|
12435
|
-
isLeaf
|
|
12436
|
-
);
|
|
12437
|
-
});
|
|
12210
|
+
const components = await this.fetchCategoryComponents(subType);
|
|
12211
|
+
return components.map((item) => createFolder(
|
|
12212
|
+
item.mediaComponentId,
|
|
12213
|
+
item.title,
|
|
12214
|
+
`/${category}/${item.mediaComponentId}`,
|
|
12215
|
+
item.imageUrls.mobileCinematicHigh || item.imageUrls.videoStill || item.imageUrls.thumbnail,
|
|
12216
|
+
item.containsCount === 0
|
|
12217
|
+
));
|
|
12438
12218
|
}
|
|
12439
12219
|
async getVideoFile(mediaComponentId) {
|
|
12440
12220
|
const variant = await this.fetchApi(
|
|
@@ -12449,11 +12229,10 @@ var JesusFilmProvider = class {
|
|
|
12449
12229
|
const title = component?.title || mediaComponentId;
|
|
12450
12230
|
const seconds = component ? Math.round(component.lengthInMilliseconds / 1e3) : 0;
|
|
12451
12231
|
const thumbnail = component?.imageUrls.mobileCinematicHigh || component?.imageUrls.videoStill;
|
|
12452
|
-
const muxPlaybackId = this.extractMuxPlaybackId(downloadUrl);
|
|
12453
12232
|
return [
|
|
12454
12233
|
createFile(mediaComponentId, title, downloadUrl, {
|
|
12455
12234
|
mediaType: "video",
|
|
12456
|
-
muxPlaybackId,
|
|
12235
|
+
muxPlaybackId: this.extractMuxPlaybackId(downloadUrl),
|
|
12457
12236
|
seconds,
|
|
12458
12237
|
thumbnail
|
|
12459
12238
|
})
|
|
@@ -12465,9 +12244,8 @@ var JesusFilmProvider = class {
|
|
|
12465
12244
|
);
|
|
12466
12245
|
const childIds = linksData.linkedMediaComponentIds?.contains;
|
|
12467
12246
|
if (!childIds || childIds.length === 0) return [];
|
|
12468
|
-
const idsParam = childIds.join(",");
|
|
12469
12247
|
const childrenData = await this.fetchApi(
|
|
12470
|
-
`/media-components?ids=${
|
|
12248
|
+
`/media-components?ids=${childIds.join(",")}&languageIds=${LANGUAGE_ID}`
|
|
12471
12249
|
);
|
|
12472
12250
|
if (!childrenData._embedded?.mediaComponents) return [];
|
|
12473
12251
|
return childrenData._embedded.mediaComponents.map((item) => createFolder(
|
|
@@ -12482,36 +12260,6 @@ var JesusFilmProvider = class {
|
|
|
12482
12260
|
const match = url.match(/stream\.mux\.com\/([^/]+)/);
|
|
12483
12261
|
return match ? match[1] : void 0;
|
|
12484
12262
|
}
|
|
12485
|
-
async getPlaylist(path, _auth, _resolution) {
|
|
12486
|
-
const { segments, depth } = parsePath(path);
|
|
12487
|
-
if (depth < 1) return null;
|
|
12488
|
-
const category = segments[0];
|
|
12489
|
-
if (depth >= 3) {
|
|
12490
|
-
const lastId = segments[depth - 1];
|
|
12491
|
-
const items = await this.getVideoFile(lastId);
|
|
12492
|
-
const files2 = items.filter((item) => item.type === "file");
|
|
12493
|
-
if (files2.length > 0) return files2;
|
|
12494
|
-
const containerFiles = await this.fetchContainerVideoFiles(lastId);
|
|
12495
|
-
return containerFiles.length > 0 ? containerFiles : null;
|
|
12496
|
-
}
|
|
12497
|
-
if (depth === 2) {
|
|
12498
|
-
if (category === "feature-films") {
|
|
12499
|
-
const items = await this.getVideoFile(segments[1]);
|
|
12500
|
-
if (items.length === 0) return null;
|
|
12501
|
-
return items.filter((item) => item.type === "file");
|
|
12502
|
-
}
|
|
12503
|
-
const files2 = await this.fetchContainerVideoFiles(segments[1]);
|
|
12504
|
-
return files2.length > 0 ? files2 : null;
|
|
12505
|
-
}
|
|
12506
|
-
const subType = CATEGORY_MAP[category];
|
|
12507
|
-
if (!subType) return null;
|
|
12508
|
-
const data = await this.fetchApi(
|
|
12509
|
-
`/media-components?filter=master&subTypes=${subType}&languageIds=${LANGUAGE_ID}&limit=100`
|
|
12510
|
-
);
|
|
12511
|
-
const contentComponents = data._embedded.mediaComponents.filter((item) => item.containsCount === 0);
|
|
12512
|
-
const files = await this.fetchVideoFiles(contentComponents);
|
|
12513
|
-
return files.length > 0 ? files : null;
|
|
12514
|
-
}
|
|
12515
12263
|
async fetchVideoFiles(components) {
|
|
12516
12264
|
const files = [];
|
|
12517
12265
|
for (const component of components) {
|
|
@@ -12542,9 +12290,8 @@ var JesusFilmProvider = class {
|
|
|
12542
12290
|
);
|
|
12543
12291
|
const childIds = linksData.linkedMediaComponentIds?.contains;
|
|
12544
12292
|
if (!childIds || childIds.length === 0) return [];
|
|
12545
|
-
const idsParam = childIds.join(",");
|
|
12546
12293
|
const childrenData = await this.fetchApi(
|
|
12547
|
-
`/media-components?ids=${
|
|
12294
|
+
`/media-components?ids=${childIds.join(",")}&languageIds=${LANGUAGE_ID}`
|
|
12548
12295
|
);
|
|
12549
12296
|
if (!childrenData._embedded?.mediaComponents) return [];
|
|
12550
12297
|
const contentItems = childrenData._embedded.mediaComponents.filter((c) => c.containsCount === 0);
|
|
@@ -12556,64 +12303,47 @@ var JesusFilmProvider = class {
|
|
|
12556
12303
|
}
|
|
12557
12304
|
return files;
|
|
12558
12305
|
}
|
|
12306
|
+
async getPlaylist(path, _auth, _resolution) {
|
|
12307
|
+
const { segments, depth } = parsePath(path);
|
|
12308
|
+
if (depth < 1) return null;
|
|
12309
|
+
const category = segments[0];
|
|
12310
|
+
if (depth >= 3) {
|
|
12311
|
+
const lastId = segments[depth - 1];
|
|
12312
|
+
const items = await this.getVideoFile(lastId);
|
|
12313
|
+
const files2 = items.filter((item) => item.type === "file");
|
|
12314
|
+
if (files2.length > 0) return files2;
|
|
12315
|
+
const containerFiles = await this.fetchContainerVideoFiles(lastId);
|
|
12316
|
+
return containerFiles.length > 0 ? containerFiles : null;
|
|
12317
|
+
}
|
|
12318
|
+
if (depth === 2) {
|
|
12319
|
+
if (category === "feature-films") {
|
|
12320
|
+
const items = await this.getVideoFile(segments[1]);
|
|
12321
|
+
if (items.length === 0) return null;
|
|
12322
|
+
return items.filter((item) => item.type === "file");
|
|
12323
|
+
}
|
|
12324
|
+
const files2 = await this.fetchContainerVideoFiles(segments[1]);
|
|
12325
|
+
return files2.length > 0 ? files2 : null;
|
|
12326
|
+
}
|
|
12327
|
+
const subType = CATEGORY_MAP[category];
|
|
12328
|
+
if (!subType) return null;
|
|
12329
|
+
const components = await this.fetchCategoryComponents(subType);
|
|
12330
|
+
const files = await this.fetchVideoFiles(components.filter((item) => item.containsCount === 0));
|
|
12331
|
+
return files.length > 0 ? files : null;
|
|
12332
|
+
}
|
|
12559
12333
|
async buildVideoInstructions(mediaComponentId, category) {
|
|
12560
12334
|
const items = await this.getVideoFile(mediaComponentId);
|
|
12561
|
-
|
|
12562
|
-
|
|
12563
|
-
|
|
12564
|
-
const actionItem = {
|
|
12565
|
-
id: mediaComponentId + "-action",
|
|
12566
|
-
itemType: "action",
|
|
12567
|
-
label: file.title,
|
|
12568
|
-
actionType: "play",
|
|
12569
|
-
children: [
|
|
12570
|
-
{
|
|
12571
|
-
id: mediaComponentId,
|
|
12572
|
-
itemType: "file",
|
|
12573
|
-
label: file.title,
|
|
12574
|
-
downloadUrl: file.url,
|
|
12575
|
-
thumbnail: file.thumbnail
|
|
12576
|
-
}
|
|
12577
|
-
]
|
|
12578
|
-
};
|
|
12579
|
-
const sectionItem = {
|
|
12580
|
-
id: category + "-section",
|
|
12581
|
-
itemType: "section",
|
|
12582
|
-
label: file.title,
|
|
12583
|
-
children: [actionItem]
|
|
12584
|
-
};
|
|
12585
|
-
return { name: file.title, items: [sectionItem] };
|
|
12335
|
+
const file = items.find((item) => item.type === "file");
|
|
12336
|
+
if (!file) return null;
|
|
12337
|
+
return filesToInstructions(file.title, [file], { id: category + "-section", label: file.title });
|
|
12586
12338
|
}
|
|
12587
|
-
async buildContainerInstructions(containerId
|
|
12339
|
+
async buildContainerInstructions(containerId) {
|
|
12588
12340
|
const containerData = await this.fetchApi(
|
|
12589
12341
|
`/media-components?ids=${containerId}&languageIds=${LANGUAGE_ID}`
|
|
12590
12342
|
);
|
|
12591
|
-
const
|
|
12592
|
-
const containerName = container?.title || containerId;
|
|
12343
|
+
const containerName = containerData._embedded?.mediaComponents?.[0]?.title || containerId;
|
|
12593
12344
|
const videoFiles = await this.fetchContainerVideoFiles(containerId);
|
|
12594
12345
|
if (videoFiles.length === 0) return { name: containerName, items: [] };
|
|
12595
|
-
|
|
12596
|
-
id: file.id + "-action",
|
|
12597
|
-
itemType: "action",
|
|
12598
|
-
label: file.title,
|
|
12599
|
-
actionType: "play",
|
|
12600
|
-
children: [
|
|
12601
|
-
{
|
|
12602
|
-
id: file.id,
|
|
12603
|
-
itemType: "file",
|
|
12604
|
-
label: file.title,
|
|
12605
|
-
downloadUrl: file.url,
|
|
12606
|
-
thumbnail: file.thumbnail
|
|
12607
|
-
}
|
|
12608
|
-
]
|
|
12609
|
-
}));
|
|
12610
|
-
const sectionItem = {
|
|
12611
|
-
id: containerId + "-section",
|
|
12612
|
-
itemType: "section",
|
|
12613
|
-
label: containerName,
|
|
12614
|
-
children: actionItems
|
|
12615
|
-
};
|
|
12616
|
-
return { name: containerName, items: [sectionItem] };
|
|
12346
|
+
return filesToInstructions(containerName, videoFiles, { id: containerId + "-section", label: containerName });
|
|
12617
12347
|
}
|
|
12618
12348
|
async getInstructions(path, _auth) {
|
|
12619
12349
|
const { segments, depth } = parsePath(path);
|
|
@@ -12622,61 +12352,19 @@ var JesusFilmProvider = class {
|
|
|
12622
12352
|
const subType = CATEGORY_MAP[category];
|
|
12623
12353
|
if (!subType) return null;
|
|
12624
12354
|
if (depth === 1) {
|
|
12625
|
-
const
|
|
12626
|
-
|
|
12627
|
-
);
|
|
12628
|
-
|
|
12629
|
-
const actionItems = await Promise.all(
|
|
12630
|
-
data._embedded.mediaComponents.map(async (component) => {
|
|
12631
|
-
let downloadUrl;
|
|
12632
|
-
if (component.containsCount === 0) {
|
|
12633
|
-
try {
|
|
12634
|
-
const variant = await this.fetchApi(
|
|
12635
|
-
`/media-components/${component.mediaComponentId}/languages/${LANGUAGE_ID}?platform=web`
|
|
12636
|
-
);
|
|
12637
|
-
downloadUrl = variant.downloadUrls.high?.url || variant.downloadUrls.low?.url;
|
|
12638
|
-
} catch {
|
|
12639
|
-
}
|
|
12640
|
-
}
|
|
12641
|
-
return {
|
|
12642
|
-
id: component.mediaComponentId + "-action",
|
|
12643
|
-
itemType: "action",
|
|
12644
|
-
label: component.title,
|
|
12645
|
-
actionType: "play",
|
|
12646
|
-
children: downloadUrl ? [
|
|
12647
|
-
{
|
|
12648
|
-
id: component.mediaComponentId,
|
|
12649
|
-
itemType: "file",
|
|
12650
|
-
label: component.title,
|
|
12651
|
-
downloadUrl,
|
|
12652
|
-
thumbnail: component.imageUrls.mobileCinematicHigh || component.imageUrls.videoStill
|
|
12653
|
-
}
|
|
12654
|
-
] : []
|
|
12655
|
-
};
|
|
12656
|
-
})
|
|
12657
|
-
);
|
|
12658
|
-
const sectionItem = {
|
|
12659
|
-
id: category + "-section",
|
|
12660
|
-
itemType: "section",
|
|
12661
|
-
label: categoryName,
|
|
12662
|
-
children: actionItems.filter((a) => a.children && a.children.length > 0)
|
|
12663
|
-
};
|
|
12664
|
-
return { name: categoryName, items: [sectionItem] };
|
|
12355
|
+
const categoryName = CATEGORY_NAMES[category];
|
|
12356
|
+
const components = await this.fetchCategoryComponents(subType);
|
|
12357
|
+
const files = await this.fetchVideoFiles(components.filter((c) => c.containsCount === 0));
|
|
12358
|
+
return filesToInstructions(categoryName, files, { id: category + "-section", label: categoryName });
|
|
12665
12359
|
}
|
|
12666
12360
|
if (depth === 2) {
|
|
12667
12361
|
if (category === "feature-films") return this.buildVideoInstructions(segments[1], category);
|
|
12668
|
-
return this.buildContainerInstructions(segments[1]
|
|
12669
|
-
}
|
|
12670
|
-
if (depth >= 3) {
|
|
12671
|
-
const lastId = segments[depth - 1];
|
|
12672
|
-
const videoResult = await this.buildVideoInstructions(lastId, category);
|
|
12673
|
-
if (videoResult) return videoResult;
|
|
12674
|
-
return this.buildContainerInstructions(lastId, category);
|
|
12362
|
+
return this.buildContainerInstructions(segments[1]);
|
|
12675
12363
|
}
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
return
|
|
12364
|
+
const lastId = segments[depth - 1];
|
|
12365
|
+
const videoResult = await this.buildVideoInstructions(lastId, category);
|
|
12366
|
+
if (videoResult) return videoResult;
|
|
12367
|
+
return this.buildContainerInstructions(lastId);
|
|
12680
12368
|
}
|
|
12681
12369
|
};
|
|
12682
12370
|
|
|
@@ -12720,35 +12408,15 @@ function convertPlaylistToFiles(playlist) {
|
|
|
12720
12408
|
return file;
|
|
12721
12409
|
});
|
|
12722
12410
|
}
|
|
12723
|
-
function convertPlaylistToInstructions(playlist) {
|
|
12724
|
-
const files = convertPlaylistToFiles(playlist);
|
|
12725
|
-
const items = files.map((file) => ({
|
|
12726
|
-
id: file.id + "-action",
|
|
12727
|
-
itemType: "action",
|
|
12728
|
-
label: file.title,
|
|
12729
|
-
actionType: "play",
|
|
12730
|
-
children: [
|
|
12731
|
-
{
|
|
12732
|
-
id: file.id,
|
|
12733
|
-
itemType: "file",
|
|
12734
|
-
label: file.title,
|
|
12735
|
-
seconds: file.seconds,
|
|
12736
|
-
downloadUrl: file.url,
|
|
12737
|
-
thumbnail: file.thumbnail
|
|
12738
|
-
}
|
|
12739
|
-
]
|
|
12740
|
-
}));
|
|
12741
|
-
return { name: playlist.lesson_title || "Lesson", items };
|
|
12742
|
-
}
|
|
12743
12411
|
|
|
12744
12412
|
// src/providers/cbn/logo.ts
|
|
12745
12413
|
var CBN_LOGO = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAMAAAC3Ycb+AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA/tpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDcuMi1jMDAwIDc5LjFiNjVhNzliNCwgMjAyMi8wNi8xMy0yMjowMTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ1dWlkOjVEMjA4OTI0OTNCRkRCMTE5MTRBODU5MEQzMTUwOEM4IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjczNTFDMjdFNENEMDExRURCNUVBQkI4RUVDOUM5MkUwIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjczNTFDMjdENENEMDExRURCNUVBQkI4RUVDOUM5MkUwIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIElsbHVzdHJhdG9yIENDIDIwMTcgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0idXVpZDo4NGJiYzc1OC1iNjBhLTFlNGQtODJlMS1mYTQyNzhlMmZhMTciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6OTQzMjNlNWMtYjBlMS00YTliLWJjZWUtODljYjRlOGVjZjAwIi8+IDxkYzp0aXRsZT4gPHJkZjpBbHQ+IDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+Q0JOLUZMQU1FPC9yZGY6bGk+IDwvcmRmOkFsdD4gPC9kYzp0aXRsZT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz40pvOUAAAC2VBMVEUdmtYhnNf8/v/7/f4emtb9/v8fm9by+f0nntjU7Pf6/f4gm9b2+/0lntfk8/q43/L3+/5Nr9/5/P4poNg1pdovotnj8voknddQsd84ptsqoNjp9fve8PnX7fgpn9hRsd/1+/3M6Pba7vhmuuO/4vMinNfG5vUtodmGyenm9Pq64PKOzOrz+f2c0+15w+fw+PyVz+zx+fyq2fCl1+9tvuREq91ateGx3PHv+Pz4/P6d0+0mnthxwOXf8fng8fkroNlhuOI3pduk1u7B4/Ts9/zq9ftAqdxHrd54w+ft9/yu2/CKy+pOsN88qNxft+I+qdy74fNIrd7n9PtDq93z+v1Ws+BrveTS6/eW0OxCq91/xuiz3fFvvuXN6fYzpNqZ0e2Ly+oxo9q94fO94vPc7/nF5fSn2O/o9ft2wuYwotk2pdtVs+Du9/ySzutqvOQjnddPsN8uotl3wubY7vjC5PSEyOl1weZpvORSsuBctuHl9Po5p9vT6/eMzOo7p9uBx+iv2/GTz+vh8vq03fFguOLs9vvV7Pe23/JXtOBkuuMon9i+4vOQzetfuOKg1e6Jyurr9vvi8vp+xuhnu+Op2e/R6vf0+v3c8Pnb7/h+xehLr946p9ssodmj1u40pNqUz+zd8PlJrt57xOdXtOFdt+Kw3PFAqtxbtuG33/J8xOd6xOet2vDI5vW54PJwv+VBqt1FrN2a0u1luuOAxuhsveSh1e6s2vCNzOqt2/Ci1e7E5PTJ5/XH5vXL6PY9qNx0weZ9xedYteHK5/WFyOmCx+hnu+TQ6vZovOS13vLD5PSb0u1Mr9/O6fay3fG84fPZ7vhiueNKrt5zwObP6vZZteFjueOm1+9TsuCf1O6o2O+DyOhUs+DF5fWPzeuX0ezW7fhGrN0wo9qOzetet+IypNqr2fC13vGY0exvv+WRzuue1O2IyumHyulywOVuvuU/qdye0+3A4/T///96A0GMAAAA83RSTlP//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wB12P98AAAWOklEQVR42uzd5YMUx9YG8JpZN3ZZdHFblg3u7u7uGtzdITgkwUJCsBCc4CRA8BASEgJxtxtPbpKrr52/4EUvK3VqWk5V9+ye5+vOdE3Xb2e6u1QAx1cRXAUMwmEQBuEwCINwGIRBOAzCIBwG4TAIg3AYhEE4DMIgHAZhEA6DcBiEQTgMwiAcBmEQDoMwCIdBOAzCIBwGYRAOgzAIh0EYhMMgHAZhEA6DMAiHQRiEwyAMwmEQDoMwCIdBsmdfcwbxVeLWt2EQX2WT2BrDID7KcCGWDWQQH2WKEOnjGcQ/eU/cyZAgg/gm9e+KfFmTQfyS8XdBRM/9DOKTpBS5J1KkPIP4JFXugYikxxjEH1kVeV8k+iKD+COHxYM0YRBf5PRDEFGLQfyQ4F7/i+Sv5vd/C9+L5C+Q/Y9AxE8M4oOUzSJyhUG8z1+ygIgVDOJ5+jXNAhJoyyCeZ0DWr0jsLQbxOqOzgohfizKIx4kokk1kcAyDeJwT2UDE0SCDeJvy2UHEmwzicdJyiFxkEG8zJAdIsYEM4ml+yAEi0qYyiKf3WS/mFNnEIJ7mXE4QMZJBvMzIXCCBWwziYT7MBSKWVWMQDy8igdwiExnEwwzODSJGM4h3eUcCMmIqg3iWJyQgoh6DeJY2MhCxgUG8ymkpSFoig3iU4VIQ8TaDeJSf5SAJcQziTYrKQcQ3DOIvELGaQfwFsqQxg3iRChiIWMkgXmQgChLbjkE8yHsoiBjCIB7kJA5SLJNBzGcfDiI6MYj5dFKABJoziPFMUYCI+QxiPBkqkEBJBjGckkKZ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";
|
|
12746
12414
|
|
|
12747
12415
|
// src/providers/cbn/CbnProvider.ts
|
|
12748
12416
|
var API_BASE5 = "https://sbamemberdev.wpenginepowered.com/wp-json/superbook/v1";
|
|
12749
|
-
var CbnProvider = class {
|
|
12417
|
+
var CbnProvider = class extends BaseProvider {
|
|
12750
12418
|
constructor() {
|
|
12751
|
-
|
|
12419
|
+
super(...arguments);
|
|
12752
12420
|
this.deviceFlowHelper = new DeviceFlowHelper();
|
|
12753
12421
|
this.id = "cbn";
|
|
12754
12422
|
this.name = "CBN";
|
|
@@ -12761,66 +12429,48 @@ var CbnProvider = class {
|
|
|
12761
12429
|
clientId: "freeplay",
|
|
12762
12430
|
scopes: [],
|
|
12763
12431
|
supportsDeviceFlow: true,
|
|
12764
|
-
deviceAuthEndpoint: "/device"
|
|
12765
|
-
endpoints: {
|
|
12766
|
-
catalog: "/catalog",
|
|
12767
|
-
courseDetail: (courseId) => `/catalog/${courseId}`,
|
|
12768
|
-
lessonPlaylist: (lessonId) => `/lesson-playlist/${lessonId}`
|
|
12769
|
-
}
|
|
12432
|
+
deviceAuthEndpoint: "/device"
|
|
12770
12433
|
};
|
|
12771
12434
|
this.requiresAuth = true;
|
|
12772
12435
|
this.authTypes = ["device_flow"];
|
|
12773
12436
|
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
12774
12437
|
}
|
|
12775
|
-
async apiRequest(path, auth) {
|
|
12776
|
-
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
12777
|
-
}
|
|
12778
12438
|
async browse(path, auth) {
|
|
12779
12439
|
const { segments, depth } = parsePath(path);
|
|
12780
12440
|
if (depth === 0) {
|
|
12781
12441
|
return [{ type: "folder", id: "catalog-root", title: "Catalog", path: "/catalog" }];
|
|
12782
12442
|
}
|
|
12783
12443
|
if (segments[0] !== "catalog") return [];
|
|
12784
|
-
if (depth === 1)
|
|
12785
|
-
|
|
12786
|
-
|
|
12444
|
+
if (depth === 1) {
|
|
12445
|
+
const response = await this.apiRequest("/catalog", auth);
|
|
12446
|
+
return Array.isArray(response) ? convertCoursesToFolders(response) : [];
|
|
12447
|
+
}
|
|
12448
|
+
if (depth === 2) {
|
|
12449
|
+
const response = await this.apiRequest(`/catalog/${segments[1]}`, auth);
|
|
12450
|
+
return response?.lessons ? convertLessonsToFolders(response.lessons, path) : [];
|
|
12451
|
+
}
|
|
12452
|
+
if (depth === 3) {
|
|
12453
|
+
const resolved = await this.resolveFiles(path, auth);
|
|
12454
|
+
return resolved?.files ?? [];
|
|
12455
|
+
}
|
|
12787
12456
|
return [];
|
|
12788
12457
|
}
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
if (!Array.isArray(response)) return [];
|
|
12792
|
-
return convertCoursesToFolders(response);
|
|
12793
|
-
}
|
|
12794
|
-
async getLessons(courseId, coursePath, auth) {
|
|
12795
|
-
const pathFn = this.config.endpoints.courseDetail;
|
|
12796
|
-
const response = await this.apiRequest(pathFn(courseId), auth);
|
|
12797
|
-
if (!response?.lessons) return [];
|
|
12798
|
-
return convertLessonsToFolders(response.lessons, coursePath);
|
|
12799
|
-
}
|
|
12800
|
-
async getVideos(lessonId, auth) {
|
|
12801
|
-
const playlist = await this.fetchPlaylist(lessonId, auth);
|
|
12802
|
-
return playlist ? convertPlaylistToFiles(playlist) : [];
|
|
12803
|
-
}
|
|
12804
|
-
async fetchPlaylist(lessonId, auth) {
|
|
12805
|
-
const pathFn = this.config.endpoints.lessonPlaylist;
|
|
12806
|
-
return this.apiRequest(pathFn(lessonId), auth);
|
|
12807
|
-
}
|
|
12808
|
-
async getPlaylist(path, auth, _resolution) {
|
|
12458
|
+
/** Resolve a leaf path (/catalog/{courseId}/{lessonId}) to its playlist files. */
|
|
12459
|
+
async resolveFiles(path, auth) {
|
|
12809
12460
|
const { segments, depth } = parsePath(path);
|
|
12810
12461
|
if (depth !== 3 || segments[0] !== "catalog") return null;
|
|
12811
|
-
const playlist = await this.
|
|
12462
|
+
const playlist = await this.apiRequest(`/lesson-playlist/${segments[2]}`, auth);
|
|
12812
12463
|
if (!playlist || playlist.playlist.length === 0) return null;
|
|
12813
|
-
return convertPlaylistToFiles(playlist);
|
|
12464
|
+
return { files: convertPlaylistToFiles(playlist), lessonTitle: playlist.lesson_title };
|
|
12814
12465
|
}
|
|
12815
|
-
async
|
|
12816
|
-
const
|
|
12817
|
-
|
|
12818
|
-
const playlist = await this.fetchPlaylist(segments[2], auth);
|
|
12819
|
-
if (!playlist || playlist.playlist.length === 0) return null;
|
|
12820
|
-
return convertPlaylistToInstructions(playlist);
|
|
12466
|
+
async getPlaylist(path, auth, _resolution) {
|
|
12467
|
+
const resolved = await this.resolveFiles(path, auth);
|
|
12468
|
+
return resolved?.files ?? null;
|
|
12821
12469
|
}
|
|
12822
|
-
|
|
12823
|
-
|
|
12470
|
+
async getInstructions(path, auth) {
|
|
12471
|
+
const resolved = await this.resolveFiles(path, auth);
|
|
12472
|
+
if (!resolved) return null;
|
|
12473
|
+
return filesToInstructions(resolved.lessonTitle || "Lesson", resolved.files);
|
|
12824
12474
|
}
|
|
12825
12475
|
async initiateDeviceFlow() {
|
|
12826
12476
|
return this.deviceFlowHelper.initiateDeviceFlow(this.config);
|
|
@@ -16451,8 +16101,9 @@ var data_default3 = {
|
|
|
16451
16101
|
};
|
|
16452
16102
|
|
|
16453
16103
|
// src/providers/lifeChurch/LifeChurchProvider.ts
|
|
16454
|
-
var LifeChurchProvider = class {
|
|
16104
|
+
var LifeChurchProvider = class extends BaseProvider {
|
|
16455
16105
|
constructor() {
|
|
16106
|
+
super(...arguments);
|
|
16456
16107
|
this.id = "lifechurch";
|
|
16457
16108
|
this.name = "Life.Church";
|
|
16458
16109
|
this.logos = {
|
|
@@ -16465,8 +16116,7 @@ var LifeChurchProvider = class {
|
|
|
16465
16116
|
apiBase: "https://www.life.church",
|
|
16466
16117
|
oauthBase: "",
|
|
16467
16118
|
clientId: "",
|
|
16468
|
-
scopes: []
|
|
16469
|
-
endpoints: {}
|
|
16119
|
+
scopes: []
|
|
16470
16120
|
};
|
|
16471
16121
|
this.requiresAuth = false;
|
|
16472
16122
|
this.authTypes = ["none"];
|
|
@@ -16481,20 +16131,14 @@ var LifeChurchProvider = class {
|
|
|
16481
16131
|
findSeries(seriesId) {
|
|
16482
16132
|
return this.data.series.find((s) => s.id === seriesId);
|
|
16483
16133
|
}
|
|
16484
|
-
findUnit(series, unitId) {
|
|
16485
|
-
return series.units.find((u) => u.id === unitId);
|
|
16486
|
-
}
|
|
16487
|
-
findLesson(unit, lessonId) {
|
|
16488
|
-
return unit.lessons.find((l) => l.id === lessonId);
|
|
16489
|
-
}
|
|
16490
16134
|
findScheduled(scheduledId) {
|
|
16491
16135
|
return this.data.scheduledLessons?.find((s) => s.id === scheduledId);
|
|
16492
16136
|
}
|
|
16493
16137
|
/** Resolve a scheduled-lesson reference to the underlying lesson + unit + series. */
|
|
16494
16138
|
resolveScheduled(sched) {
|
|
16495
16139
|
const series = this.findSeries(sched.seriesId);
|
|
16496
|
-
const unit = series
|
|
16497
|
-
const lesson = unit
|
|
16140
|
+
const unit = series?.units.find((u) => u.id === sched.unitId);
|
|
16141
|
+
const lesson = unit?.lessons.find((l) => l.id === sched.lessonId);
|
|
16498
16142
|
if (!series || !unit || !lesson) return null;
|
|
16499
16143
|
return { series, unit, lesson };
|
|
16500
16144
|
}
|
|
@@ -16509,17 +16153,40 @@ var LifeChurchProvider = class {
|
|
|
16509
16153
|
providerData: { sourceUrl: lesson.sourceUrl }
|
|
16510
16154
|
};
|
|
16511
16155
|
}
|
|
16156
|
+
/** Resolve a leaf-or-unit path to its files plus the name/section used for instructions. */
|
|
16157
|
+
resolve(path) {
|
|
16158
|
+
const { segments, depth } = parsePath(path);
|
|
16159
|
+
if (segments[0] === "scheduled") {
|
|
16160
|
+
if (depth !== 2) return null;
|
|
16161
|
+
const sched = this.findScheduled(segments[1]);
|
|
16162
|
+
const resolved = sched && this.resolveScheduled(sched);
|
|
16163
|
+
if (!sched || !resolved) return null;
|
|
16164
|
+
const label = `${sched.ageGroup} \u2014 ${resolved.lesson.title}`;
|
|
16165
|
+
return {
|
|
16166
|
+
files: [this.lessonToFile(resolved.lesson, label)],
|
|
16167
|
+
name: label,
|
|
16168
|
+
section: { id: `scheduled-${sched.id}-section`, label: "This Sunday" }
|
|
16169
|
+
};
|
|
16170
|
+
}
|
|
16171
|
+
const series = this.findSeries(segments[0]);
|
|
16172
|
+
const unit = series?.units.find((u) => u.id === segments[1]);
|
|
16173
|
+
if (!unit) return null;
|
|
16174
|
+
if (depth === 2) {
|
|
16175
|
+
return { files: unit.lessons.map((l) => this.lessonToFile(l)), name: unit.name, section: { id: `${unit.id}-section`, label: unit.name } };
|
|
16176
|
+
}
|
|
16177
|
+
if (depth === 3) {
|
|
16178
|
+
const lesson = unit.lessons.find((l) => l.id === segments[2]);
|
|
16179
|
+
if (!lesson) return null;
|
|
16180
|
+
return { files: [this.lessonToFile(lesson)], name: lesson.title, section: { id: `${unit.id}-section`, label: unit.name } };
|
|
16181
|
+
}
|
|
16182
|
+
return null;
|
|
16183
|
+
}
|
|
16512
16184
|
async browse(path, _auth) {
|
|
16513
16185
|
const { segments, depth } = parsePath(path);
|
|
16514
16186
|
if (depth === 0) {
|
|
16515
16187
|
const items = [];
|
|
16516
16188
|
if (this.data.scheduledLessons && this.data.scheduledLessons.length > 0) {
|
|
16517
|
-
items.push({
|
|
16518
|
-
type: "folder",
|
|
16519
|
-
id: "scheduled",
|
|
16520
|
-
title: "This Sunday",
|
|
16521
|
-
path: "/scheduled"
|
|
16522
|
-
});
|
|
16189
|
+
items.push({ type: "folder", id: "scheduled", title: "This Sunday", path: "/scheduled" });
|
|
16523
16190
|
}
|
|
16524
16191
|
for (const s of this.data.series) {
|
|
16525
16192
|
items.push({ type: "folder", id: s.id, title: s.name, thumbnail: s.thumbnail, path: `/${s.id}` });
|
|
@@ -16540,13 +16207,7 @@ var LifeChurchProvider = class {
|
|
|
16540
16207
|
};
|
|
16541
16208
|
});
|
|
16542
16209
|
}
|
|
16543
|
-
|
|
16544
|
-
const sched = this.findScheduled(segments[1]);
|
|
16545
|
-
const resolved = sched && this.resolveScheduled(sched);
|
|
16546
|
-
if (!sched || !resolved) return [];
|
|
16547
|
-
return [this.lessonToFile(resolved.lesson, `${sched.ageGroup} \u2014 ${resolved.lesson.title}`)];
|
|
16548
|
-
}
|
|
16549
|
-
return [];
|
|
16210
|
+
return this.resolve(path)?.files ?? [];
|
|
16550
16211
|
}
|
|
16551
16212
|
if (depth === 1) {
|
|
16552
16213
|
const series = this.findSeries(segments[0]);
|
|
@@ -16561,8 +16222,8 @@ var LifeChurchProvider = class {
|
|
|
16561
16222
|
}
|
|
16562
16223
|
if (depth === 2) {
|
|
16563
16224
|
const series = this.findSeries(segments[0]);
|
|
16564
|
-
const unit = series
|
|
16565
|
-
if (!
|
|
16225
|
+
const unit = series?.units.find((u) => u.id === segments[1]);
|
|
16226
|
+
if (!unit) return [];
|
|
16566
16227
|
return unit.lessons.map((l) => ({
|
|
16567
16228
|
type: "folder",
|
|
16568
16229
|
id: l.id,
|
|
@@ -16573,78 +16234,18 @@ var LifeChurchProvider = class {
|
|
|
16573
16234
|
}));
|
|
16574
16235
|
}
|
|
16575
16236
|
if (depth === 3) {
|
|
16576
|
-
|
|
16577
|
-
const unit = series && this.findUnit(series, segments[1]);
|
|
16578
|
-
const lesson = unit && this.findLesson(unit, segments[2]);
|
|
16579
|
-
if (!lesson) return [];
|
|
16580
|
-
return [this.lessonToFile(lesson)];
|
|
16237
|
+
return this.resolve(path)?.files ?? [];
|
|
16581
16238
|
}
|
|
16582
16239
|
return [];
|
|
16583
16240
|
}
|
|
16584
16241
|
async getPlaylist(path, _auth, _resolution) {
|
|
16585
|
-
const
|
|
16586
|
-
|
|
16587
|
-
const sched = this.findScheduled(segments[1]);
|
|
16588
|
-
const resolved = sched && this.resolveScheduled(sched);
|
|
16589
|
-
return resolved ? [this.lessonToFile(resolved.lesson, `${sched.ageGroup} \u2014 ${resolved.lesson.title}`)] : null;
|
|
16590
|
-
}
|
|
16591
|
-
if (depth === 3) {
|
|
16592
|
-
const series = this.findSeries(segments[0]);
|
|
16593
|
-
const unit = series && this.findUnit(series, segments[1]);
|
|
16594
|
-
const lesson = unit && this.findLesson(unit, segments[2]);
|
|
16595
|
-
return lesson ? [this.lessonToFile(lesson)] : null;
|
|
16596
|
-
}
|
|
16597
|
-
if (depth === 2 && segments[0] !== "scheduled") {
|
|
16598
|
-
const series = this.findSeries(segments[0]);
|
|
16599
|
-
const unit = series && this.findUnit(series, segments[1]);
|
|
16600
|
-
if (!unit) return null;
|
|
16601
|
-
const files = unit.lessons.map((l) => this.lessonToFile(l));
|
|
16602
|
-
return files.length > 0 ? files : null;
|
|
16603
|
-
}
|
|
16604
|
-
return null;
|
|
16242
|
+
const resolved = this.resolve(path);
|
|
16243
|
+
return resolved && resolved.files.length > 0 ? resolved.files : null;
|
|
16605
16244
|
}
|
|
16606
16245
|
async getInstructions(path, _auth) {
|
|
16607
|
-
const
|
|
16608
|
-
|
|
16609
|
-
|
|
16610
|
-
itemType: "action",
|
|
16611
|
-
label: label ?? lesson.title,
|
|
16612
|
-
actionType: "play",
|
|
16613
|
-
children: [
|
|
16614
|
-
{
|
|
16615
|
-
id: lesson.id,
|
|
16616
|
-
itemType: "file",
|
|
16617
|
-
label: label ?? lesson.title,
|
|
16618
|
-
downloadUrl: lesson.videoUrl,
|
|
16619
|
-
thumbnail: lesson.thumbnail,
|
|
16620
|
-
mediaType: "video"
|
|
16621
|
-
}
|
|
16622
|
-
]
|
|
16623
|
-
});
|
|
16624
|
-
if (segments[0] === "scheduled" && depth === 2) {
|
|
16625
|
-
const sched = this.findScheduled(segments[1]);
|
|
16626
|
-
const resolved = sched && this.resolveScheduled(sched);
|
|
16627
|
-
if (!sched || !resolved) return null;
|
|
16628
|
-
const label = `${sched.ageGroup} \u2014 ${resolved.lesson.title}`;
|
|
16629
|
-
return {
|
|
16630
|
-
name: label,
|
|
16631
|
-
items: [{ id: `scheduled-${sched.id}-section`, itemType: "section", label: "This Sunday", children: [buildItem(resolved.lesson, label)] }]
|
|
16632
|
-
};
|
|
16633
|
-
}
|
|
16634
|
-
if (depth === 3) {
|
|
16635
|
-
const series = this.findSeries(segments[0]);
|
|
16636
|
-
const unit = series && this.findUnit(series, segments[1]);
|
|
16637
|
-
const lesson = unit && this.findLesson(unit, segments[2]);
|
|
16638
|
-
if (!series || !unit || !lesson) return null;
|
|
16639
|
-
return { name: lesson.title, items: [{ id: `${unit.id}-section`, itemType: "section", label: unit.name, children: [buildItem(lesson)] }] };
|
|
16640
|
-
}
|
|
16641
|
-
if (depth === 2 && segments[0] !== "scheduled") {
|
|
16642
|
-
const series = this.findSeries(segments[0]);
|
|
16643
|
-
const unit = series && this.findUnit(series, segments[1]);
|
|
16644
|
-
if (!unit) return null;
|
|
16645
|
-
return { name: unit.name, items: [{ id: `${unit.id}-section`, itemType: "section", label: unit.name, children: unit.lessons.map((l) => buildItem(l)) }] };
|
|
16646
|
-
}
|
|
16647
|
-
return null;
|
|
16246
|
+
const resolved = this.resolve(path);
|
|
16247
|
+
if (!resolved) return null;
|
|
16248
|
+
return filesToInstructions(resolved.name, resolved.files, resolved.section);
|
|
16648
16249
|
}
|
|
16649
16250
|
/**
|
|
16650
16251
|
* Returns the most recent scheduled lesson from data.scheduledLessons.
|
|
@@ -16672,9 +16273,6 @@ var LifeChurchProvider = class {
|
|
|
16672
16273
|
]
|
|
16673
16274
|
};
|
|
16674
16275
|
}
|
|
16675
|
-
supportsDeviceFlow() {
|
|
16676
|
-
return false;
|
|
16677
|
-
}
|
|
16678
16276
|
};
|
|
16679
16277
|
|
|
16680
16278
|
// src/providers/index.ts
|
|
@@ -16769,18 +16367,21 @@ function getAvailableProviders(ids) {
|
|
|
16769
16367
|
}
|
|
16770
16368
|
|
|
16771
16369
|
// src/index.ts
|
|
16772
|
-
var VERSION = true ? "0.
|
|
16370
|
+
var VERSION = true ? "0.5.0" : "dev";
|
|
16773
16371
|
// Annotate the CommonJS export names for ESM import in node:
|
|
16774
16372
|
0 && (module.exports = {
|
|
16775
16373
|
APlayProvider,
|
|
16776
16374
|
B1ChurchProvider,
|
|
16375
|
+
BaseProvider,
|
|
16777
16376
|
BibleProjectProvider,
|
|
16778
16377
|
CbnProvider,
|
|
16378
|
+
DeviceFlowHelper,
|
|
16779
16379
|
DropboxProvider,
|
|
16780
16380
|
HighVoltageKidsProvider,
|
|
16781
16381
|
JesusFilmProvider,
|
|
16782
16382
|
LessonsChurchProvider,
|
|
16783
16383
|
LifeChurchProvider,
|
|
16384
|
+
OAuthHelper,
|
|
16784
16385
|
PlanningCenterProvider,
|
|
16785
16386
|
SignPresenterProvider,
|
|
16786
16387
|
TokenHelper,
|
|
@@ -16792,6 +16393,7 @@ var VERSION = true ? "0.4.1" : "dev";
|
|
|
16792
16393
|
isContentFile,
|
|
16793
16394
|
isContentFolder,
|
|
16794
16395
|
navigateToPath,
|
|
16795
|
-
registerProvider
|
|
16396
|
+
registerProvider,
|
|
16397
|
+
toAuthData
|
|
16796
16398
|
});
|
|
16797
16399
|
//# sourceMappingURL=index.cjs.map
|