@churchapps/content-providers 0.4.1 → 0.5.1
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 +441 -846
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +78 -148
- package/dist/index.d.ts +78 -148
- package/dist/index.js +436 -845
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -18,33 +18,81 @@ function navigateToPath(instructions, path) {
|
|
|
18
18
|
return current;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// src/helpers/TokenHelper.ts
|
|
22
|
+
function toAuthData(data, fallbacks) {
|
|
23
|
+
return {
|
|
24
|
+
access_token: data.access_token,
|
|
25
|
+
refresh_token: data.refresh_token || fallbacks?.refreshToken || "",
|
|
26
|
+
token_type: data.token_type || "Bearer",
|
|
27
|
+
created_at: Math.floor(Date.now() / 1e3),
|
|
28
|
+
expires_in: data.expires_in,
|
|
29
|
+
scope: data.scope || fallbacks?.scope || ""
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
var TokenHelper = class {
|
|
33
|
+
isAuthValid(auth) {
|
|
34
|
+
if (!auth) return false;
|
|
35
|
+
return !this.isTokenExpired(auth);
|
|
36
|
+
}
|
|
37
|
+
isTokenExpired(auth) {
|
|
38
|
+
if (!auth.created_at || !auth.expires_in) return true;
|
|
39
|
+
const expiresAt = (auth.created_at + auth.expires_in) * 1e3;
|
|
40
|
+
return Date.now() > expiresAt - 5 * 60 * 1e3;
|
|
41
|
+
}
|
|
42
|
+
async refreshToken(config, auth) {
|
|
43
|
+
if (!auth.refresh_token) return null;
|
|
44
|
+
try {
|
|
45
|
+
const params = new URLSearchParams({
|
|
46
|
+
grant_type: "refresh_token",
|
|
47
|
+
refresh_token: auth.refresh_token,
|
|
48
|
+
client_id: config.clientId
|
|
49
|
+
});
|
|
50
|
+
const response = await fetch(`${config.oauthBase}/token`, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: params.toString() });
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
console.warn(`[TokenHelper] Token refresh failed: ${response.status} ${response.statusText}`);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const data = await response.json();
|
|
56
|
+
return toAuthData(data, { refreshToken: auth.refresh_token, scope: auth.scope });
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
21
63
|
// src/helpers/OAuthHelper.ts
|
|
64
|
+
function generateCodeVerifier() {
|
|
65
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
|
|
66
|
+
const length = 64;
|
|
67
|
+
const array = new Uint8Array(length);
|
|
68
|
+
crypto.getRandomValues(array);
|
|
69
|
+
let result = "";
|
|
70
|
+
for (let i = 0; i < length; i++) {
|
|
71
|
+
result += chars.charAt(array[i] % chars.length);
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
async function generateCodeChallenge(verifier) {
|
|
76
|
+
const encoder = new TextEncoder();
|
|
77
|
+
const data = encoder.encode(verifier);
|
|
78
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
79
|
+
const hashArray = new Uint8Array(hashBuffer);
|
|
80
|
+
let binary = "";
|
|
81
|
+
for (let i = 0; i < hashArray.length; i++) {
|
|
82
|
+
binary += String.fromCharCode(hashArray[i]);
|
|
83
|
+
}
|
|
84
|
+
const base64 = btoa(binary);
|
|
85
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
86
|
+
}
|
|
22
87
|
var OAuthHelper = class {
|
|
23
88
|
generateCodeVerifier() {
|
|
24
|
-
|
|
25
|
-
const length = 64;
|
|
26
|
-
const array = new Uint8Array(length);
|
|
27
|
-
crypto.getRandomValues(array);
|
|
28
|
-
let result = "";
|
|
29
|
-
for (let i = 0; i < length; i++) {
|
|
30
|
-
result += chars.charAt(array[i] % chars.length);
|
|
31
|
-
}
|
|
32
|
-
return result;
|
|
89
|
+
return generateCodeVerifier();
|
|
33
90
|
}
|
|
34
91
|
async generateCodeChallenge(verifier) {
|
|
35
|
-
|
|
36
|
-
const data = encoder.encode(verifier);
|
|
37
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
38
|
-
const hashArray = new Uint8Array(hashBuffer);
|
|
39
|
-
let binary = "";
|
|
40
|
-
for (let i = 0; i < hashArray.length; i++) {
|
|
41
|
-
binary += String.fromCharCode(hashArray[i]);
|
|
42
|
-
}
|
|
43
|
-
const base64 = btoa(binary);
|
|
44
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
92
|
+
return generateCodeChallenge(verifier);
|
|
45
93
|
}
|
|
46
94
|
async buildAuthUrl(config, codeVerifier, redirectUri, state) {
|
|
47
|
-
const codeChallenge = await
|
|
95
|
+
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
48
96
|
const params = new URLSearchParams({
|
|
49
97
|
response_type: "code",
|
|
50
98
|
client_id: config.clientId,
|
|
@@ -71,39 +119,7 @@ var OAuthHelper = class {
|
|
|
71
119
|
return null;
|
|
72
120
|
}
|
|
73
121
|
const data = await response.json();
|
|
74
|
-
return
|
|
75
|
-
} catch {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// src/helpers/TokenHelper.ts
|
|
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 { 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 };
|
|
122
|
+
return toAuthData(data, { scope: config.scopes.join(" ") });
|
|
107
123
|
} catch {
|
|
108
124
|
return null;
|
|
109
125
|
}
|
|
@@ -130,7 +146,7 @@ var DeviceFlowHelper = class {
|
|
|
130
146
|
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 }) });
|
|
131
147
|
if (response.ok) {
|
|
132
148
|
const data = await response.json();
|
|
133
|
-
return
|
|
149
|
+
return toAuthData(data, { scope: config.scopes.join(" ") });
|
|
134
150
|
}
|
|
135
151
|
const errorData = await response.json();
|
|
136
152
|
switch (errorData.error) {
|
|
@@ -180,6 +196,19 @@ var ApiHelper = class {
|
|
|
180
196
|
}
|
|
181
197
|
};
|
|
182
198
|
|
|
199
|
+
// src/providers/BaseProvider.ts
|
|
200
|
+
var BaseProvider = class {
|
|
201
|
+
constructor() {
|
|
202
|
+
this.apiHelper = new ApiHelper();
|
|
203
|
+
}
|
|
204
|
+
apiRequest(path, auth, method = "GET", body) {
|
|
205
|
+
return this.apiHelper.apiRequest(this.config, this.id, path, auth, method, body);
|
|
206
|
+
}
|
|
207
|
+
supportsDeviceFlow() {
|
|
208
|
+
return !!this.config.supportsDeviceFlow && !!this.config.deviceAuthEndpoint;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
183
212
|
// src/pathUtils.ts
|
|
184
213
|
function parsePath(path) {
|
|
185
214
|
if (!path || path === "/" || path === "") {
|
|
@@ -193,26 +222,6 @@ function getSegment(path, index) {
|
|
|
193
222
|
return segments[index] ?? null;
|
|
194
223
|
}
|
|
195
224
|
|
|
196
|
-
// src/providers/aPlay/APlayApi.ts
|
|
197
|
-
var API_BASE = "https://api-prod.amazingkids.app";
|
|
198
|
-
async function checkMediaLicense(mediaId, auth) {
|
|
199
|
-
if (!auth) return null;
|
|
200
|
-
try {
|
|
201
|
-
const url = `${API_BASE}/prod/reports/media/license-check`;
|
|
202
|
-
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] }) });
|
|
203
|
-
if (!response.ok) return null;
|
|
204
|
-
const data = await response.json();
|
|
205
|
-
const licenseData = Array.isArray(data) ? data : data.data || [];
|
|
206
|
-
const result = licenseData.find((item) => item.mediaId === mediaId);
|
|
207
|
-
if (result?.isLicensed) {
|
|
208
|
-
return { mediaId, status: "valid", message: "Media is licensed for playback", expiresAt: result.expiresAt };
|
|
209
|
-
}
|
|
210
|
-
return { mediaId, status: "not_licensed", message: "Media is not licensed" };
|
|
211
|
-
} catch {
|
|
212
|
-
return { mediaId, status: "unknown", message: "Unable to verify license status" };
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
225
|
// src/utils.ts
|
|
217
226
|
var IMAGE_DURATION_SECONDS = 15;
|
|
218
227
|
function slugify(text) {
|
|
@@ -250,6 +259,31 @@ function createFile(id, title, url, options) {
|
|
|
250
259
|
function generateId() {
|
|
251
260
|
return "gen-" + Math.random().toString(36).substring(2, 11);
|
|
252
261
|
}
|
|
262
|
+
function fileToActionItem(file) {
|
|
263
|
+
return {
|
|
264
|
+
id: file.id + "-action",
|
|
265
|
+
itemType: "action",
|
|
266
|
+
label: file.title,
|
|
267
|
+
actionType: "play",
|
|
268
|
+
seconds: file.seconds || void 0,
|
|
269
|
+
children: [
|
|
270
|
+
{
|
|
271
|
+
id: file.id,
|
|
272
|
+
itemType: "file",
|
|
273
|
+
label: file.title,
|
|
274
|
+
seconds: file.seconds || void 0,
|
|
275
|
+
downloadUrl: file.downloadUrl || file.url,
|
|
276
|
+
thumbnail: file.thumbnail,
|
|
277
|
+
mediaType: file.mediaType
|
|
278
|
+
}
|
|
279
|
+
]
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function filesToInstructions(name, files, section) {
|
|
283
|
+
const actions = files.map(fileToActionItem);
|
|
284
|
+
const items = section ? [{ id: section.id, itemType: "section", label: section.label ?? name, children: actions }] : actions;
|
|
285
|
+
return { name, items };
|
|
286
|
+
}
|
|
253
287
|
function instructionsToPlaylist(instructions) {
|
|
254
288
|
const files = [];
|
|
255
289
|
function extractFiles(items) {
|
|
@@ -264,6 +298,26 @@ function instructionsToPlaylist(instructions) {
|
|
|
264
298
|
return files;
|
|
265
299
|
}
|
|
266
300
|
|
|
301
|
+
// src/providers/aPlay/APlayApi.ts
|
|
302
|
+
var API_BASE = "https://api-prod.amazingkids.app";
|
|
303
|
+
async function checkMediaLicense(mediaId, auth) {
|
|
304
|
+
if (!auth) return null;
|
|
305
|
+
try {
|
|
306
|
+
const url = `${API_BASE}/prod/reports/media/license-check`;
|
|
307
|
+
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] }) });
|
|
308
|
+
if (!response.ok) return null;
|
|
309
|
+
const data = await response.json();
|
|
310
|
+
const licenseData = Array.isArray(data) ? data : data.data || [];
|
|
311
|
+
const result = licenseData.find((item) => item.mediaId === mediaId);
|
|
312
|
+
if (result?.isLicensed) {
|
|
313
|
+
return { mediaId, status: "valid", message: "Media is licensed for playback", expiresAt: result.expiresAt };
|
|
314
|
+
}
|
|
315
|
+
return { mediaId, status: "not_licensed", message: "Media is not licensed" };
|
|
316
|
+
} catch {
|
|
317
|
+
return { mediaId, status: "unknown", message: "Unable to verify license status" };
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
267
321
|
// src/providers/aPlay/APlayConverters.ts
|
|
268
322
|
function extractLibraryId(path) {
|
|
269
323
|
const { segments, depth } = parsePath(path);
|
|
@@ -330,25 +384,6 @@ function convertProductsToFolders(products, currentPath) {
|
|
|
330
384
|
return createFolder(id, p.title || p.name, `${currentPath}/products/${id}`, p.image);
|
|
331
385
|
});
|
|
332
386
|
}
|
|
333
|
-
function convertFilesToInstructions(files, _libraryId) {
|
|
334
|
-
const items = files.map((file) => ({
|
|
335
|
-
id: file.id + "-action",
|
|
336
|
-
itemType: "action",
|
|
337
|
-
label: file.title,
|
|
338
|
-
actionType: "play",
|
|
339
|
-
children: [
|
|
340
|
-
{
|
|
341
|
-
id: file.id,
|
|
342
|
-
itemType: "file",
|
|
343
|
-
label: file.title,
|
|
344
|
-
seconds: file.seconds,
|
|
345
|
-
downloadUrl: file.url,
|
|
346
|
-
thumbnail: file.thumbnail
|
|
347
|
-
}
|
|
348
|
-
]
|
|
349
|
-
}));
|
|
350
|
-
return { name: "Library", items };
|
|
351
|
-
}
|
|
352
387
|
|
|
353
388
|
// src/providers/aPlay/APlayProvider.ts
|
|
354
389
|
function extractArray(response, ...keys) {
|
|
@@ -361,20 +396,17 @@ function extractArray(response, ...keys) {
|
|
|
361
396
|
}
|
|
362
397
|
return [];
|
|
363
398
|
}
|
|
364
|
-
var APlayProvider = class {
|
|
399
|
+
var APlayProvider = class extends BaseProvider {
|
|
365
400
|
constructor() {
|
|
366
|
-
|
|
401
|
+
super(...arguments);
|
|
367
402
|
this.id = "aplay";
|
|
368
403
|
this.name = "APlay";
|
|
369
404
|
this.logos = { light: "https://www.joinamazing.com/_assets/v11/3ba846c5afd7e73d27bc4d87b63d423e7ae2dc73.svg", dark: "https://www.joinamazing.com/_assets/v11/3ba846c5afd7e73d27bc4d87b63d423e7ae2dc73.svg" };
|
|
370
|
-
this.config = { id: "aplay", name: "APlay", apiBase: API_BASE, oauthBase: "https://api.joinamazing.com/prod/aims/oauth", clientId: "xFJFq7yNYuXXXMx0YBiQ", scopes: ["openid", "profile", "email"]
|
|
405
|
+
this.config = { id: "aplay", name: "APlay", apiBase: API_BASE, oauthBase: "https://api.joinamazing.com/prod/aims/oauth", clientId: "xFJFq7yNYuXXXMx0YBiQ", scopes: ["openid", "profile", "email"] };
|
|
371
406
|
this.requiresAuth = true;
|
|
372
407
|
this.authTypes = ["oauth_pkce"];
|
|
373
408
|
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: true };
|
|
374
409
|
}
|
|
375
|
-
async apiRequest(path, auth) {
|
|
376
|
-
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
377
|
-
}
|
|
378
410
|
async browse(path, auth) {
|
|
379
411
|
const { segments, depth } = parsePath(path);
|
|
380
412
|
if (depth === 0) {
|
|
@@ -390,12 +422,12 @@ var APlayProvider = class {
|
|
|
390
422
|
return [];
|
|
391
423
|
}
|
|
392
424
|
async getModules(auth) {
|
|
393
|
-
const response = await this.apiRequest(
|
|
425
|
+
const response = await this.apiRequest("/prod/curriculum/modules", auth);
|
|
394
426
|
const modules = extractArray(response, "data", "modules");
|
|
395
427
|
return convertModulesToFolders(modules);
|
|
396
428
|
}
|
|
397
429
|
async getModuleContent(moduleId, currentPath, auth) {
|
|
398
|
-
const response = await this.apiRequest(
|
|
430
|
+
const response = await this.apiRequest("/prod/curriculum/modules", auth);
|
|
399
431
|
const modules = extractArray(response, "data", "modules");
|
|
400
432
|
const module = modules.find((m) => (m.id || m.moduleId) === moduleId);
|
|
401
433
|
if (!module) return [];
|
|
@@ -411,14 +443,12 @@ var APlayProvider = class {
|
|
|
411
443
|
}
|
|
412
444
|
}
|
|
413
445
|
async getLibraryFolders(productId, currentPath, auth) {
|
|
414
|
-
const
|
|
415
|
-
const response = await this.apiRequest(pathFn(productId), auth);
|
|
446
|
+
const response = await this.apiRequest(`/prod/curriculum/modules/products/${productId}/libraries`, auth);
|
|
416
447
|
const libraries = extractArray(response, "data", "libraries");
|
|
417
448
|
return convertLibrariesToFolders(libraries, currentPath);
|
|
418
449
|
}
|
|
419
450
|
async getMediaFiles(libraryId, auth) {
|
|
420
|
-
const
|
|
421
|
-
const response = await this.apiRequest(pathFn(libraryId), auth);
|
|
451
|
+
const response = await this.apiRequest(`/prod/creators/libraries/${libraryId}/media`, auth);
|
|
422
452
|
const mediaItems = extractArray(response, "data", "media");
|
|
423
453
|
return convertMediaToFiles(mediaItems);
|
|
424
454
|
}
|
|
@@ -433,63 +463,47 @@ var APlayProvider = class {
|
|
|
433
463
|
if (!libraryId) return null;
|
|
434
464
|
const files = await this.getMediaFiles(libraryId, auth);
|
|
435
465
|
if (files.length === 0) return null;
|
|
436
|
-
return
|
|
466
|
+
return filesToInstructions("Library", files);
|
|
437
467
|
}
|
|
438
468
|
async checkMediaLicense(mediaId, auth) {
|
|
439
469
|
return checkMediaLicense(mediaId, auth);
|
|
440
470
|
}
|
|
441
|
-
supportsDeviceFlow() {
|
|
442
|
-
return false;
|
|
443
|
-
}
|
|
444
471
|
};
|
|
445
472
|
|
|
446
473
|
// src/providers/signPresenter/SignPresenterProvider.ts
|
|
447
|
-
var SignPresenterProvider = class {
|
|
474
|
+
var SignPresenterProvider = class extends BaseProvider {
|
|
448
475
|
constructor() {
|
|
449
|
-
|
|
476
|
+
super(...arguments);
|
|
450
477
|
this.oauthHelper = new OAuthHelper();
|
|
451
478
|
this.deviceFlowHelper = new DeviceFlowHelper();
|
|
452
479
|
this.id = "signpresenter";
|
|
453
480
|
this.name = "SignPresenter";
|
|
454
481
|
this.logos = { light: "https://signpresenter.com/files/shared/images/logo.png", dark: "https://signpresenter.com/files/shared/images/logo.png" };
|
|
455
|
-
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"
|
|
482
|
+
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" };
|
|
456
483
|
this.requiresAuth = true;
|
|
457
484
|
this.authTypes = ["oauth_pkce", "device_flow"];
|
|
458
485
|
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
459
486
|
}
|
|
460
|
-
async apiRequest(path, auth) {
|
|
461
|
-
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
462
|
-
}
|
|
463
487
|
async browse(path, auth) {
|
|
464
488
|
const { segments, depth } = parsePath(path);
|
|
465
489
|
if (depth === 0) {
|
|
466
|
-
return [
|
|
467
|
-
{
|
|
468
|
-
type: "folder",
|
|
469
|
-
id: "playlists-root",
|
|
470
|
-
title: "Playlists",
|
|
471
|
-
path: "/playlists"
|
|
472
|
-
}
|
|
473
|
-
];
|
|
474
|
-
}
|
|
475
|
-
const root = segments[0];
|
|
476
|
-
if (root !== "playlists") return [];
|
|
477
|
-
if (depth === 1) {
|
|
478
|
-
return this.getPlaylists(auth);
|
|
479
|
-
}
|
|
480
|
-
if (depth === 2) {
|
|
481
|
-
const playlistId = segments[1];
|
|
482
|
-
return this.getMessages(playlistId, auth);
|
|
490
|
+
return [{ type: "folder", id: "playlists-root", title: "Playlists", path: "/playlists" }];
|
|
483
491
|
}
|
|
492
|
+
if (segments[0] !== "playlists") return [];
|
|
493
|
+
if (depth === 1) return this.getPlaylists(auth);
|
|
494
|
+
if (depth === 2) return this.getMessages(segments[1], auth);
|
|
484
495
|
return [];
|
|
485
496
|
}
|
|
497
|
+
extractList(response, key) {
|
|
498
|
+
if (Array.isArray(response)) return response;
|
|
499
|
+
const record = response;
|
|
500
|
+
const list = record?.data || record?.[key] || [];
|
|
501
|
+
return Array.isArray(list) ? list : [];
|
|
502
|
+
}
|
|
486
503
|
async getPlaylists(auth) {
|
|
487
|
-
const
|
|
488
|
-
const response = await this.apiRequest(apiPath, auth);
|
|
504
|
+
const response = await this.apiRequest("/content/playlists", auth);
|
|
489
505
|
if (!response) return [];
|
|
490
|
-
|
|
491
|
-
if (!Array.isArray(playlists)) return [];
|
|
492
|
-
return playlists.map((p) => ({
|
|
506
|
+
return this.extractList(response, "playlists").map((p) => ({
|
|
493
507
|
type: "folder",
|
|
494
508
|
id: p.id,
|
|
495
509
|
title: p.name,
|
|
@@ -499,26 +513,17 @@ var SignPresenterProvider = class {
|
|
|
499
513
|
}));
|
|
500
514
|
}
|
|
501
515
|
async getMessages(playlistId, auth) {
|
|
502
|
-
const
|
|
503
|
-
const response = await this.apiRequest(pathFn(playlistId), auth);
|
|
516
|
+
const response = await this.apiRequest(`/content/playlists/${playlistId}/messages`, auth);
|
|
504
517
|
if (!response) return [];
|
|
505
|
-
const messages = Array.isArray(response) ? response : response.data || response.messages || [];
|
|
506
|
-
if (!Array.isArray(messages)) return [];
|
|
507
518
|
const files = [];
|
|
508
|
-
for (const msg of messages) {
|
|
519
|
+
for (const msg of this.extractList(response, "messages")) {
|
|
509
520
|
if (!msg.url) continue;
|
|
510
521
|
const url = msg.url;
|
|
511
|
-
const
|
|
512
|
-
|
|
513
|
-
msg.
|
|
514
|
-
msg.
|
|
515
|
-
|
|
516
|
-
{
|
|
517
|
-
mediaType: detectMediaType(url, msg.mediaType),
|
|
518
|
-
thumbnail: msg.thumbnail || msg.image,
|
|
519
|
-
seconds
|
|
520
|
-
}
|
|
521
|
-
);
|
|
522
|
+
const file = createFile(msg.id, msg.name, url, {
|
|
523
|
+
mediaType: detectMediaType(url, msg.mediaType),
|
|
524
|
+
thumbnail: msg.thumbnail || msg.image,
|
|
525
|
+
seconds: msg.seconds
|
|
526
|
+
});
|
|
522
527
|
file.downloadUrl = url;
|
|
523
528
|
files.push(file);
|
|
524
529
|
}
|
|
@@ -527,8 +532,7 @@ var SignPresenterProvider = class {
|
|
|
527
532
|
async getPlaylist(path, auth, _resolution) {
|
|
528
533
|
const { segments, depth } = parsePath(path);
|
|
529
534
|
if (depth < 2 || segments[0] !== "playlists") return null;
|
|
530
|
-
const
|
|
531
|
-
const files = await this.getMessages(playlistId, auth);
|
|
535
|
+
const files = await this.getMessages(segments[1], auth);
|
|
532
536
|
return files.length > 0 ? files : null;
|
|
533
537
|
}
|
|
534
538
|
async getInstructions(path, auth) {
|
|
@@ -538,42 +542,9 @@ var SignPresenterProvider = class {
|
|
|
538
542
|
const files = await this.getMessages(playlistId, auth);
|
|
539
543
|
if (files.length === 0) return null;
|
|
540
544
|
const playlists = await this.getPlaylists(auth);
|
|
541
|
-
const
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
id: file.id + "-action",
|
|
545
|
-
itemType: "action",
|
|
546
|
-
label: file.title,
|
|
547
|
-
actionType: "play",
|
|
548
|
-
seconds: file.seconds,
|
|
549
|
-
children: [
|
|
550
|
-
{
|
|
551
|
-
id: file.id,
|
|
552
|
-
itemType: "file",
|
|
553
|
-
label: file.title,
|
|
554
|
-
seconds: file.seconds,
|
|
555
|
-
downloadUrl: file.downloadUrl || file.url,
|
|
556
|
-
thumbnail: file.thumbnail
|
|
557
|
-
}
|
|
558
|
-
]
|
|
559
|
-
}));
|
|
560
|
-
const sectionItem = {
|
|
561
|
-
id: playlistId + "-section",
|
|
562
|
-
itemType: "section",
|
|
563
|
-
label: title,
|
|
564
|
-
children: actionItems
|
|
565
|
-
};
|
|
566
|
-
const headerItem = {
|
|
567
|
-
id: playlistId + "-header",
|
|
568
|
-
itemType: "header",
|
|
569
|
-
label: title,
|
|
570
|
-
children: [sectionItem]
|
|
571
|
-
};
|
|
572
|
-
return { name: title, items: [headerItem] };
|
|
573
|
-
}
|
|
574
|
-
// Auth methods
|
|
575
|
-
supportsDeviceFlow() {
|
|
576
|
-
return this.deviceFlowHelper.supportsDeviceFlow(this.config);
|
|
545
|
+
const title = playlists.find((p) => p.id === playlistId)?.title || "Playlist";
|
|
546
|
+
const instructions = filesToInstructions(title, files, { id: playlistId + "-section", label: title });
|
|
547
|
+
return { name: title, items: [{ id: playlistId + "-header", itemType: "header", label: title, children: instructions.items }] };
|
|
577
548
|
}
|
|
578
549
|
generateCodeVerifier() {
|
|
579
550
|
return this.oauthHelper.generateCodeVerifier();
|
|
@@ -621,6 +592,23 @@ function truncateForLabel(text) {
|
|
|
621
592
|
const truncated = text.length > cutoff ? text.substring(0, cutoff) : text;
|
|
622
593
|
return stripMarkdown(truncated) + (text.length > cutoff ? "..." : "");
|
|
623
594
|
}
|
|
595
|
+
function getEmbedUrl(apiType, relatedId) {
|
|
596
|
+
if (!relatedId) return void 0;
|
|
597
|
+
const baseUrl = "https://lessons.church";
|
|
598
|
+
switch (apiType) {
|
|
599
|
+
case "action":
|
|
600
|
+
case "lessonAction":
|
|
601
|
+
return `${baseUrl}/embed/action/${relatedId}`;
|
|
602
|
+
case "addon":
|
|
603
|
+
case "lessonAddOn":
|
|
604
|
+
return `${baseUrl}/embed/addon/${relatedId}`;
|
|
605
|
+
case "section":
|
|
606
|
+
case "lessonSection":
|
|
607
|
+
return `${baseUrl}/embed/section/${relatedId}`;
|
|
608
|
+
default:
|
|
609
|
+
return void 0;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
624
612
|
async function convertAddOnToFile(addOn) {
|
|
625
613
|
const apiPath = `/addOns/public/${addOn.id}`;
|
|
626
614
|
const detail = await apiRequest(apiPath);
|
|
@@ -767,12 +755,13 @@ async function convertAddOnCategoryToInstructions(category) {
|
|
|
767
755
|
}
|
|
768
756
|
|
|
769
757
|
// src/providers/lessonsChurch/LessonsChurchProvider.ts
|
|
770
|
-
var LessonsChurchProvider = class {
|
|
758
|
+
var LessonsChurchProvider = class extends BaseProvider {
|
|
771
759
|
constructor() {
|
|
760
|
+
super(...arguments);
|
|
772
761
|
this.id = "lessonschurch";
|
|
773
762
|
this.name = "Lessons.church";
|
|
774
763
|
this.logos = { light: "https://lessons.church/images/logo.png", dark: "https://lessons.church/images/logo-dark.png" };
|
|
775
|
-
this.config = { id: "lessonschurch", name: "Lessons.church", apiBase: API_BASE2, oauthBase: "", clientId: "", scopes: []
|
|
764
|
+
this.config = { id: "lessonschurch", name: "Lessons.church", apiBase: API_BASE2, oauthBase: "", clientId: "", scopes: [] };
|
|
776
765
|
this.requiresAuth = false;
|
|
777
766
|
this.authTypes = ["none"];
|
|
778
767
|
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
@@ -828,28 +817,25 @@ var LessonsChurchProvider = class {
|
|
|
828
817
|
return [];
|
|
829
818
|
}
|
|
830
819
|
async getPrograms() {
|
|
831
|
-
const response = await apiRequest(
|
|
820
|
+
const response = await apiRequest("/programs/public");
|
|
832
821
|
if (!response) return [];
|
|
833
822
|
const programs = Array.isArray(response) ? response : [];
|
|
834
823
|
return programs.map((p) => createFolder(p.id, p.name, `/lessons/${p.id}`, p.image));
|
|
835
824
|
}
|
|
836
825
|
async getStudies(programId, currentPath) {
|
|
837
|
-
const
|
|
838
|
-
const response = await apiRequest(pathFn(programId));
|
|
826
|
+
const response = await apiRequest(`/studies/public/program/${programId}`);
|
|
839
827
|
if (!response) return [];
|
|
840
828
|
const studies = Array.isArray(response) ? response : [];
|
|
841
829
|
return studies.map((s) => createFolder(s.id, s.name, `${currentPath}/${s.id}`, s.image)).sort((a, b) => a.title.localeCompare(b.title));
|
|
842
830
|
}
|
|
843
831
|
async getLessons(studyId, currentPath) {
|
|
844
|
-
const
|
|
845
|
-
const response = await apiRequest(pathFn(studyId));
|
|
832
|
+
const response = await apiRequest(`/lessons/public/study/${studyId}`);
|
|
846
833
|
if (!response) return [];
|
|
847
834
|
const lessons = Array.isArray(response) ? response : [];
|
|
848
835
|
return lessons.map((l) => createFolder(l.id, l.name || l.title, `${currentPath}/${l.id}`, l.image));
|
|
849
836
|
}
|
|
850
837
|
async getVenues(lessonId, currentPath) {
|
|
851
|
-
const
|
|
852
|
-
const response = await apiRequest(pathFn(lessonId));
|
|
838
|
+
const response = await apiRequest(`/venues/public/lesson/${lessonId}`);
|
|
853
839
|
if (!response) return [];
|
|
854
840
|
const lessonResponse = await apiRequest(`/lessons/public/${lessonId}`);
|
|
855
841
|
const lessonImage = lessonResponse?.image;
|
|
@@ -872,7 +858,7 @@ var LessonsChurchProvider = class {
|
|
|
872
858
|
return file ? [file] : [];
|
|
873
859
|
}
|
|
874
860
|
async getAddOnCategories() {
|
|
875
|
-
const response = await apiRequest(
|
|
861
|
+
const response = await apiRequest("/addOns/public");
|
|
876
862
|
if (!response) return [];
|
|
877
863
|
const addOns = Array.isArray(response) ? response : [];
|
|
878
864
|
const categories = Array.from(new Set(addOns.map((a) => a.category).filter(Boolean)));
|
|
@@ -880,7 +866,7 @@ var LessonsChurchProvider = class {
|
|
|
880
866
|
}
|
|
881
867
|
async getAddOnsByCategory(category, currentPath) {
|
|
882
868
|
const decodedCategory = decodeURIComponent(category);
|
|
883
|
-
const response = await apiRequest(
|
|
869
|
+
const response = await apiRequest("/addOns/public");
|
|
884
870
|
if (!response) return [];
|
|
885
871
|
const allAddOns = Array.isArray(response) ? response : [];
|
|
886
872
|
const filtered = allAddOns.filter((a) => a.category === decodedCategory);
|
|
@@ -923,9 +909,6 @@ var LessonsChurchProvider = class {
|
|
|
923
909
|
}
|
|
924
910
|
return null;
|
|
925
911
|
}
|
|
926
|
-
supportsDeviceFlow() {
|
|
927
|
-
return false;
|
|
928
|
-
}
|
|
929
912
|
};
|
|
930
913
|
|
|
931
914
|
// src/providers/registry.ts
|
|
@@ -941,18 +924,6 @@ function registerProvider(provider) {
|
|
|
941
924
|
}
|
|
942
925
|
|
|
943
926
|
// src/providers/b1Church/B1ChurchAuth.ts
|
|
944
|
-
async function generateCodeChallenge(verifier) {
|
|
945
|
-
const encoder = new TextEncoder();
|
|
946
|
-
const data = encoder.encode(verifier);
|
|
947
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
948
|
-
const hashArray = new Uint8Array(hashBuffer);
|
|
949
|
-
let binary = "";
|
|
950
|
-
for (let i = 0; i < hashArray.length; i++) {
|
|
951
|
-
binary += String.fromCharCode(hashArray[i]);
|
|
952
|
-
}
|
|
953
|
-
const base64 = btoa(binary);
|
|
954
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
955
|
-
}
|
|
956
927
|
async function buildB1AuthUrl(config, appBase, redirectUri, codeVerifier, state) {
|
|
957
928
|
const codeChallenge = await generateCodeChallenge(codeVerifier);
|
|
958
929
|
const oauthParams = new URLSearchParams({
|
|
@@ -976,7 +947,7 @@ async function exchangeCodeForTokensWithPKCE(config, code, redirectUri, codeVeri
|
|
|
976
947
|
return null;
|
|
977
948
|
}
|
|
978
949
|
const data = await response.json();
|
|
979
|
-
return
|
|
950
|
+
return toAuthData(data, { scope: config.scopes.join(" ") });
|
|
980
951
|
} catch {
|
|
981
952
|
return null;
|
|
982
953
|
}
|
|
@@ -990,7 +961,7 @@ async function exchangeCodeForTokensWithSecret(config, code, redirectUri, client
|
|
|
990
961
|
return null;
|
|
991
962
|
}
|
|
992
963
|
const data = await response.json();
|
|
993
|
-
return
|
|
964
|
+
return toAuthData(data, { scope: config.scopes.join(" ") });
|
|
994
965
|
} catch {
|
|
995
966
|
return null;
|
|
996
967
|
}
|
|
@@ -1002,7 +973,7 @@ async function refreshTokenWithSecret(config, auth, clientSecret) {
|
|
|
1002
973
|
const response = await fetch(`${config.oauthBase}/token`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(params) });
|
|
1003
974
|
if (!response.ok) return null;
|
|
1004
975
|
const data = await response.json();
|
|
1005
|
-
return
|
|
976
|
+
return toAuthData(data, { refreshToken: auth.refresh_token, scope: auth.scope });
|
|
1006
977
|
} catch {
|
|
1007
978
|
return null;
|
|
1008
979
|
}
|
|
@@ -1027,78 +998,32 @@ async function authFetch(url, auth) {
|
|
|
1027
998
|
}
|
|
1028
999
|
}
|
|
1029
1000
|
async function fetchMinistries(auth) {
|
|
1030
|
-
|
|
1031
|
-
return result || [];
|
|
1001
|
+
return await authFetch(`${API_BASE3}/membership/groups/tag/ministry`, auth) || [];
|
|
1032
1002
|
}
|
|
1033
1003
|
async function fetchPlanTypes(ministryId, auth) {
|
|
1034
|
-
|
|
1035
|
-
return result || [];
|
|
1004
|
+
return await authFetch(`${API_BASE3}/doing/planTypes/ministryId/${ministryId}`, auth) || [];
|
|
1036
1005
|
}
|
|
1037
1006
|
async function fetchPlans(planTypeId, auth) {
|
|
1038
|
-
|
|
1039
|
-
return result || [];
|
|
1007
|
+
return await authFetch(`${API_BASE3}/doing/plans/types/${planTypeId}`, auth) || [];
|
|
1040
1008
|
}
|
|
1041
|
-
var DOING_API_BASE = "https://api.churchapps.org/doing";
|
|
1042
1009
|
async function fetchCurrentPlanByType(planTypeId) {
|
|
1043
|
-
|
|
1044
|
-
const response = await fetch(`${DOING_API_BASE}/plans/public/current/${planTypeId}`, { method: "GET", headers: { Accept: "application/json" } });
|
|
1045
|
-
if (!response.ok) {
|
|
1046
|
-
console.warn(`[B1Church] fetchCurrentPlanByType failed: HTTP ${response.status} for planTypeId=${planTypeId}`);
|
|
1047
|
-
return null;
|
|
1048
|
-
}
|
|
1049
|
-
return await response.json();
|
|
1050
|
-
} catch (err) {
|
|
1051
|
-
console.error(`[B1Church] fetchCurrentPlanByType error: planTypeId=${planTypeId}`, err);
|
|
1052
|
-
return null;
|
|
1053
|
-
}
|
|
1010
|
+
return authFetch(`${API_BASE3}/doing/plans/public/current/${planTypeId}`);
|
|
1054
1011
|
}
|
|
1055
1012
|
async function fetchPlanItems(churchId, planId) {
|
|
1056
|
-
|
|
1057
|
-
const response = await fetch(`${DOING_API_BASE}/planItems/presenter/${churchId}/${planId}`, { method: "GET", headers: { Accept: "application/json" } });
|
|
1058
|
-
if (!response.ok) return [];
|
|
1059
|
-
return await response.json() || [];
|
|
1060
|
-
} catch {
|
|
1061
|
-
return [];
|
|
1062
|
-
}
|
|
1013
|
+
return await authFetch(`${API_BASE3}/doing/planItems/presenter/${churchId}/${planId}`) || [];
|
|
1063
1014
|
}
|
|
1064
1015
|
async function fetchVenueFeed(venueId) {
|
|
1065
|
-
|
|
1066
|
-
const response = await fetch(`${LESSONS_API_BASE}/venues/public/feed/${venueId}`, { method: "GET", headers: { Accept: "application/json" } });
|
|
1067
|
-
if (!response.ok) {
|
|
1068
|
-
console.warn(`[B1Church] fetchVenueFeed failed: HTTP ${response.status} for venueId=${venueId}`);
|
|
1069
|
-
return null;
|
|
1070
|
-
}
|
|
1071
|
-
return await response.json();
|
|
1072
|
-
} catch (err) {
|
|
1073
|
-
console.error(`[B1Church] fetchVenueFeed error: venueId=${venueId}`, err);
|
|
1074
|
-
return null;
|
|
1075
|
-
}
|
|
1016
|
+
return authFetch(`${LESSONS_API_BASE}/venues/public/feed/${venueId}`);
|
|
1076
1017
|
}
|
|
1077
1018
|
async function fetchVenueActions(venueId) {
|
|
1078
|
-
|
|
1079
|
-
const response = await fetch(`${LESSONS_API_BASE}/venues/public/actions/${venueId}`, { method: "GET", headers: { Accept: "application/json" } });
|
|
1080
|
-
if (!response.ok) {
|
|
1081
|
-
console.warn(`[B1Church] fetchVenueActions failed: HTTP ${response.status} for venueId=${venueId}`);
|
|
1082
|
-
return null;
|
|
1083
|
-
}
|
|
1084
|
-
return await response.json();
|
|
1085
|
-
} catch (err) {
|
|
1086
|
-
console.error(`[B1Church] fetchVenueActions error: venueId=${venueId}`, err);
|
|
1087
|
-
return null;
|
|
1088
|
-
}
|
|
1019
|
+
return authFetch(`${LESSONS_API_BASE}/venues/public/actions/${venueId}`);
|
|
1089
1020
|
}
|
|
1090
1021
|
async function fetchVenueImages(venueIds) {
|
|
1091
1022
|
const map = /* @__PURE__ */ new Map();
|
|
1092
1023
|
if (venueIds.length === 0) return map;
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
if (!response.ok) return map;
|
|
1097
|
-
const data = await response.json();
|
|
1098
|
-
for (const row of data) {
|
|
1099
|
-
if (row.venueId && row.lessonImage) map.set(row.venueId, row.lessonImage);
|
|
1100
|
-
}
|
|
1101
|
-
} catch {
|
|
1024
|
+
const data = await authFetch(`${LESSONS_API_BASE}/venues/public/images?ids=${venueIds.join(",")}`);
|
|
1025
|
+
for (const row of data || []) {
|
|
1026
|
+
if (row.venueId && row.lessonImage) map.set(row.venueId, row.lessonImage);
|
|
1102
1027
|
}
|
|
1103
1028
|
return map;
|
|
1104
1029
|
}
|
|
@@ -1187,18 +1112,7 @@ function getFileFromProviderFileItem(item) {
|
|
|
1187
1112
|
};
|
|
1188
1113
|
}
|
|
1189
1114
|
function planItemToInstruction(item, thumbnail) {
|
|
1190
|
-
|
|
1191
|
-
switch (item.itemType) {
|
|
1192
|
-
case "lessonSection":
|
|
1193
|
-
itemType = "section";
|
|
1194
|
-
break;
|
|
1195
|
-
case "lessonAction":
|
|
1196
|
-
itemType = "action";
|
|
1197
|
-
break;
|
|
1198
|
-
case "lessonAddOn":
|
|
1199
|
-
itemType = "action";
|
|
1200
|
-
break;
|
|
1201
|
-
}
|
|
1115
|
+
const itemType = normalizeItemType(item.itemType);
|
|
1202
1116
|
const isFileType = itemType === "file" || item.link && !item.children?.length;
|
|
1203
1117
|
return {
|
|
1204
1118
|
id: item.id,
|
|
@@ -1212,23 +1126,6 @@ function planItemToInstruction(item, thumbnail) {
|
|
|
1212
1126
|
children: item.children?.map((child) => planItemToInstruction(child, thumbnail))
|
|
1213
1127
|
};
|
|
1214
1128
|
}
|
|
1215
|
-
function getEmbedUrl(apiType, relatedId) {
|
|
1216
|
-
if (!relatedId) return void 0;
|
|
1217
|
-
const baseUrl = "https://lessons.church";
|
|
1218
|
-
switch (apiType) {
|
|
1219
|
-
case "action":
|
|
1220
|
-
case "lessonAction":
|
|
1221
|
-
return `${baseUrl}/embed/action/${relatedId}`;
|
|
1222
|
-
case "addon":
|
|
1223
|
-
case "lessonAddOn":
|
|
1224
|
-
return `${baseUrl}/embed/addon/${relatedId}`;
|
|
1225
|
-
case "section":
|
|
1226
|
-
case "lessonSection":
|
|
1227
|
-
return `${baseUrl}/embed/section/${relatedId}`;
|
|
1228
|
-
default:
|
|
1229
|
-
return void 0;
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
1129
|
function buildSectionActionsMap2(actionsResponse, thumbnail) {
|
|
1233
1130
|
const sectionActionsMap = /* @__PURE__ */ new Map();
|
|
1234
1131
|
if (actionsResponse?.sections) {
|
|
@@ -1342,9 +1239,9 @@ function isExternalProviderItem(item) {
|
|
|
1342
1239
|
const itemType = item.itemType || "";
|
|
1343
1240
|
return itemType.startsWith("provider");
|
|
1344
1241
|
}
|
|
1345
|
-
var B1ChurchProvider = class {
|
|
1242
|
+
var B1ChurchProvider = class extends BaseProvider {
|
|
1346
1243
|
constructor() {
|
|
1347
|
-
|
|
1244
|
+
super(...arguments);
|
|
1348
1245
|
this.deviceFlowHelper = new DeviceFlowHelper();
|
|
1349
1246
|
// Unified cache for external provider data to avoid duplicate calls across methods
|
|
1350
1247
|
this.externalContentCache = {
|
|
@@ -1355,16 +1252,13 @@ var B1ChurchProvider = class {
|
|
|
1355
1252
|
this.id = "b1church";
|
|
1356
1253
|
this.name = "B1.Church";
|
|
1357
1254
|
this.logos = { light: "https://b1.church/b1-church-logo.png", dark: "https://b1.church/b1-church-logo.png" };
|
|
1358
|
-
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"
|
|
1255
|
+
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" };
|
|
1359
1256
|
this.appBase = "https://admin.b1.church";
|
|
1360
1257
|
this.requiresAuth = true;
|
|
1361
1258
|
this.authTypes = ["oauth_pkce", "device_flow"];
|
|
1362
1259
|
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
1363
1260
|
this.planTypeId = null;
|
|
1364
1261
|
}
|
|
1365
|
-
async apiRequest(path, authData) {
|
|
1366
|
-
return this.apiHelper.apiRequest(this.config, this.id, path, authData);
|
|
1367
|
-
}
|
|
1368
1262
|
async buildAuthUrl(codeVerifier, redirectUri, state) {
|
|
1369
1263
|
return buildB1AuthUrl(this.config, this.appBase, redirectUri, codeVerifier, state);
|
|
1370
1264
|
}
|
|
@@ -1510,8 +1404,7 @@ var B1ChurchProvider = class {
|
|
|
1510
1404
|
console.warn("[B1Church getInstructions] planFolder missing churchId:", planFolder.id);
|
|
1511
1405
|
return null;
|
|
1512
1406
|
}
|
|
1513
|
-
const
|
|
1514
|
-
const planItems = await this.apiRequest(pathFn(churchId, planId), authData);
|
|
1407
|
+
const planItems = await this.apiRequest(`/planFeed/presenter/${churchId}/${planId}`, authData);
|
|
1515
1408
|
if ((!planItems || planItems.length === 0) && planFolder.providerId && planFolder.providerPlanId) {
|
|
1516
1409
|
const externalInstructions = await fetchFromProviderProxy(
|
|
1517
1410
|
"getInstructions",
|
|
@@ -1619,8 +1512,7 @@ var B1ChurchProvider = class {
|
|
|
1619
1512
|
console.warn("[B1Church] getPlaylist: planFolder missing churchId:", planFolder.id);
|
|
1620
1513
|
return null;
|
|
1621
1514
|
}
|
|
1622
|
-
const
|
|
1623
|
-
const planItems = await this.apiRequest(pathFn(churchId, planId), authData);
|
|
1515
|
+
const planItems = await this.apiRequest(`/planFeed/presenter/${churchId}/${planId}`, authData);
|
|
1624
1516
|
if ((!planItems || planItems.length === 0) && planFolder.providerId && planFolder.providerPlanId) {
|
|
1625
1517
|
const externalFiles = await fetchFromProviderProxy(
|
|
1626
1518
|
"getPlaylist",
|
|
@@ -1715,9 +1607,6 @@ var B1ChurchProvider = class {
|
|
|
1715
1607
|
}
|
|
1716
1608
|
return files;
|
|
1717
1609
|
}
|
|
1718
|
-
supportsDeviceFlow() {
|
|
1719
|
-
return !!this.config.supportsDeviceFlow;
|
|
1720
|
-
}
|
|
1721
1610
|
};
|
|
1722
1611
|
|
|
1723
1612
|
// src/providers/dropbox/DropboxConverters.ts
|
|
@@ -1743,38 +1632,13 @@ function filterMediaEntries(entries) {
|
|
|
1743
1632
|
}
|
|
1744
1633
|
return { folders, mediaFiles };
|
|
1745
1634
|
}
|
|
1746
|
-
function buildInstructionsFromFiles(files, folderName) {
|
|
1747
|
-
const actionItems = files.map((file) => ({
|
|
1748
|
-
id: file.id + "-action",
|
|
1749
|
-
itemType: "action",
|
|
1750
|
-
label: file.title,
|
|
1751
|
-
actionType: "play",
|
|
1752
|
-
children: [
|
|
1753
|
-
{
|
|
1754
|
-
id: file.id,
|
|
1755
|
-
itemType: "file",
|
|
1756
|
-
label: file.title,
|
|
1757
|
-
seconds: file.seconds,
|
|
1758
|
-
downloadUrl: file.downloadUrl || file.url,
|
|
1759
|
-
thumbnail: file.thumbnail
|
|
1760
|
-
}
|
|
1761
|
-
]
|
|
1762
|
-
}));
|
|
1763
|
-
const sectionItem = {
|
|
1764
|
-
id: "dropbox-section",
|
|
1765
|
-
itemType: "section",
|
|
1766
|
-
label: folderName,
|
|
1767
|
-
children: actionItems
|
|
1768
|
-
};
|
|
1769
|
-
return { name: folderName, items: [sectionItem] };
|
|
1770
|
-
}
|
|
1771
1635
|
|
|
1772
1636
|
// src/providers/dropbox/DropboxProvider.ts
|
|
1773
1637
|
var MAX_PAGINATION_CALLS = 50;
|
|
1774
|
-
var DropboxProvider = class {
|
|
1638
|
+
var DropboxProvider = class extends BaseProvider {
|
|
1775
1639
|
constructor() {
|
|
1640
|
+
super(...arguments);
|
|
1776
1641
|
this.oauthHelper = new OAuthHelper();
|
|
1777
|
-
this.apiHelper = new ApiHelper();
|
|
1778
1642
|
this.id = "dropbox";
|
|
1779
1643
|
this.name = "Dropbox";
|
|
1780
1644
|
this.logos = {
|
|
@@ -1787,12 +1651,7 @@ var DropboxProvider = class {
|
|
|
1787
1651
|
apiBase: "https://api.dropboxapi.com",
|
|
1788
1652
|
oauthBase: "https://www.dropbox.com/oauth2",
|
|
1789
1653
|
clientId: "9io0q0q9angdz9j",
|
|
1790
|
-
scopes: []
|
|
1791
|
-
endpoints: {
|
|
1792
|
-
listFolder: "/2/files/list_folder",
|
|
1793
|
-
listFolderContinue: "/2/files/list_folder/continue",
|
|
1794
|
-
getTemporaryLink: "/2/files/get_temporary_link"
|
|
1795
|
-
}
|
|
1654
|
+
scopes: []
|
|
1796
1655
|
};
|
|
1797
1656
|
this.requiresAuth = true;
|
|
1798
1657
|
this.authTypes = ["oauth_pkce"];
|
|
@@ -1803,18 +1662,17 @@ var DropboxProvider = class {
|
|
|
1803
1662
|
mediaLicensing: false
|
|
1804
1663
|
};
|
|
1805
1664
|
}
|
|
1806
|
-
// -- Pagination helper --
|
|
1807
1665
|
dropboxPost(endpoint, body, auth) {
|
|
1808
1666
|
if (!auth) {
|
|
1809
1667
|
console.error("[Dropbox] No auth token provided");
|
|
1810
1668
|
return Promise.resolve(null);
|
|
1811
1669
|
}
|
|
1812
|
-
return this.
|
|
1670
|
+
return this.apiRequest(endpoint, auth, "POST", body);
|
|
1813
1671
|
}
|
|
1814
1672
|
async listAllEntries(dropboxPath, auth) {
|
|
1815
1673
|
const allEntries = [];
|
|
1816
1674
|
const response = await this.dropboxPost(
|
|
1817
|
-
|
|
1675
|
+
"/2/files/list_folder",
|
|
1818
1676
|
{ path: dropboxPath, recursive: false, include_media_info: false },
|
|
1819
1677
|
auth
|
|
1820
1678
|
);
|
|
@@ -1825,7 +1683,7 @@ var DropboxProvider = class {
|
|
|
1825
1683
|
let calls = 0;
|
|
1826
1684
|
while (hasMore && calls < MAX_PAGINATION_CALLS) {
|
|
1827
1685
|
const cont = await this.dropboxPost(
|
|
1828
|
-
|
|
1686
|
+
"/2/files/list_folder/continue",
|
|
1829
1687
|
{ cursor },
|
|
1830
1688
|
auth
|
|
1831
1689
|
);
|
|
@@ -1837,7 +1695,6 @@ var DropboxProvider = class {
|
|
|
1837
1695
|
}
|
|
1838
1696
|
return allEntries;
|
|
1839
1697
|
}
|
|
1840
|
-
// -- File link helpers --
|
|
1841
1698
|
async getSharedLink(filePath, auth) {
|
|
1842
1699
|
if (!auth) return null;
|
|
1843
1700
|
try {
|
|
@@ -1875,7 +1732,7 @@ var DropboxProvider = class {
|
|
|
1875
1732
|
}
|
|
1876
1733
|
async getTemporaryLink(filePath, auth) {
|
|
1877
1734
|
const response = await this.dropboxPost(
|
|
1878
|
-
|
|
1735
|
+
"/2/files/get_temporary_link",
|
|
1879
1736
|
{ path: filePath },
|
|
1880
1737
|
auth
|
|
1881
1738
|
);
|
|
@@ -1893,17 +1750,15 @@ var DropboxProvider = class {
|
|
|
1893
1750
|
);
|
|
1894
1751
|
return results.filter((r) => r.viewUrl || r.downloadUrl).map((r) => fileEntryToContentFile(r.entry, r.viewUrl || r.downloadUrl, r.downloadUrl));
|
|
1895
1752
|
}
|
|
1896
|
-
// -- Leaf detection --
|
|
1897
1753
|
async checkIsLeaf(folderPath, auth) {
|
|
1898
1754
|
const response = await this.dropboxPost(
|
|
1899
|
-
|
|
1755
|
+
"/2/files/list_folder",
|
|
1900
1756
|
{ path: folderPath, recursive: false, include_media_info: false },
|
|
1901
1757
|
auth
|
|
1902
1758
|
);
|
|
1903
1759
|
if (!response) return false;
|
|
1904
1760
|
return !response.entries.some((e) => e[".tag"] === "folder");
|
|
1905
1761
|
}
|
|
1906
|
-
// -- IProvider methods --
|
|
1907
1762
|
async browse(path, auth) {
|
|
1908
1763
|
const dropboxPath = path && path !== "/" ? path.startsWith("/") ? path : "/" + path : "";
|
|
1909
1764
|
const allEntries = await this.listAllEntries(dropboxPath, auth);
|
|
@@ -1915,30 +1770,26 @@ var DropboxProvider = class {
|
|
|
1915
1770
|
const fileItems = await this.resolveFileLinks(mediaFiles, auth);
|
|
1916
1771
|
return [...folderItems, ...fileItems];
|
|
1917
1772
|
}
|
|
1918
|
-
async
|
|
1773
|
+
async resolveMediaFiles(path, auth) {
|
|
1919
1774
|
if (!path || path === "/") return null;
|
|
1920
1775
|
const dropboxPath = path.startsWith("/") ? path : "/" + path;
|
|
1921
1776
|
const allEntries = await this.listAllEntries(dropboxPath, auth);
|
|
1922
1777
|
const { mediaFiles } = filterMediaEntries(allEntries);
|
|
1923
1778
|
if (mediaFiles.length === 0) return null;
|
|
1924
1779
|
const files = await this.resolveFileLinks(mediaFiles, auth);
|
|
1925
|
-
|
|
1780
|
+
if (files.length === 0) return null;
|
|
1781
|
+
return { files, dropboxPath };
|
|
1782
|
+
}
|
|
1783
|
+
async getPlaylist(path, auth, _resolution) {
|
|
1784
|
+
const resolved = await this.resolveMediaFiles(path, auth);
|
|
1785
|
+
return resolved?.files ?? null;
|
|
1926
1786
|
}
|
|
1927
1787
|
async getInstructions(path, auth) {
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
const
|
|
1931
|
-
const { mediaFiles } = filterMediaEntries(allEntries);
|
|
1932
|
-
if (mediaFiles.length === 0) return null;
|
|
1933
|
-
const files = await this.resolveFileLinks(mediaFiles, auth);
|
|
1934
|
-
if (files.length === 0) return null;
|
|
1935
|
-
const segments = dropboxPath.split("/").filter(Boolean);
|
|
1788
|
+
const resolved = await this.resolveMediaFiles(path, auth);
|
|
1789
|
+
if (!resolved) return null;
|
|
1790
|
+
const segments = resolved.dropboxPath.split("/").filter(Boolean);
|
|
1936
1791
|
const folderName = segments[segments.length - 1] || "Dropbox";
|
|
1937
|
-
return
|
|
1938
|
-
}
|
|
1939
|
-
// -- Auth methods --
|
|
1940
|
-
supportsDeviceFlow() {
|
|
1941
|
-
return false;
|
|
1792
|
+
return filesToInstructions(folderName, resolved.files, { id: "dropbox-section", label: folderName });
|
|
1942
1793
|
}
|
|
1943
1794
|
generateCodeVerifier() {
|
|
1944
1795
|
return this.oauthHelper.generateCodeVerifier();
|
|
@@ -1976,61 +1827,43 @@ var DropboxProvider = class {
|
|
|
1976
1827
|
body: params.toString()
|
|
1977
1828
|
});
|
|
1978
1829
|
if (!response.ok) return null;
|
|
1979
|
-
|
|
1980
|
-
return {
|
|
1981
|
-
access_token: data.access_token,
|
|
1982
|
-
refresh_token: data.refresh_token,
|
|
1983
|
-
token_type: data.token_type || "Bearer",
|
|
1984
|
-
created_at: Math.floor(Date.now() / 1e3),
|
|
1985
|
-
expires_in: data.expires_in,
|
|
1986
|
-
scope: data.scope || ""
|
|
1987
|
-
};
|
|
1830
|
+
return toAuthData(await response.json());
|
|
1988
1831
|
} catch {
|
|
1989
1832
|
return null;
|
|
1990
1833
|
}
|
|
1991
1834
|
}
|
|
1992
1835
|
};
|
|
1993
1836
|
|
|
1994
|
-
// src/providers/planningCenter/
|
|
1995
|
-
var
|
|
1837
|
+
// src/providers/planningCenter/PlanningCenterProvider.ts
|
|
1838
|
+
var ONE_WEEK_MS = 6048e5;
|
|
1996
1839
|
function formatDate(dateString) {
|
|
1997
|
-
|
|
1998
|
-
return date.toISOString().slice(0, 10);
|
|
1840
|
+
return new Date(dateString).toISOString().slice(0, 10);
|
|
1999
1841
|
}
|
|
2000
|
-
|
|
2001
|
-
// src/providers/planningCenter/PlanningCenterProvider.ts
|
|
2002
|
-
var PlanningCenterProvider = class {
|
|
1842
|
+
var PlanningCenterProvider = class extends BaseProvider {
|
|
2003
1843
|
constructor() {
|
|
2004
|
-
|
|
1844
|
+
super(...arguments);
|
|
2005
1845
|
this.id = "planningcenter";
|
|
2006
1846
|
this.name = "Planning Center";
|
|
2007
1847
|
this.logos = { light: "https://www.planningcenter.com/icons/icon-512x512.png", dark: "https://www.planningcenter.com/icons/icon-512x512.png" };
|
|
2008
|
-
this.config = { id: "planningcenter", name: "Planning Center", apiBase: "https://api.planningcenteronline.com", oauthBase: "https://api.planningcenteronline.com/oauth", clientId: "", scopes: ["services"]
|
|
2009
|
-
this.ONE_WEEK_MS = 6048e5;
|
|
1848
|
+
this.config = { id: "planningcenter", name: "Planning Center", apiBase: "https://api.planningcenteronline.com", oauthBase: "https://api.planningcenteronline.com/oauth", clientId: "", scopes: ["services"] };
|
|
2010
1849
|
this.requiresAuth = true;
|
|
2011
1850
|
this.authTypes = ["oauth_pkce"];
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
async apiRequest(path, auth) {
|
|
2015
|
-
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
1851
|
+
// Plan items come back without media URLs, so playlist/instructions are not actually supported yet.
|
|
1852
|
+
this.capabilities = { browse: true, playlist: false, instructions: false, mediaLicensing: false };
|
|
2016
1853
|
}
|
|
2017
1854
|
async browse(path, auth) {
|
|
2018
1855
|
const { segments, depth } = parsePath(path);
|
|
2019
1856
|
if (depth === 0) {
|
|
2020
1857
|
return [{ type: "folder", id: "serviceTypes-root", title: "Service Types", path: "/serviceTypes" }];
|
|
2021
1858
|
}
|
|
2022
|
-
|
|
2023
|
-
if (root !== "serviceTypes") return [];
|
|
1859
|
+
if (segments[0] !== "serviceTypes") return [];
|
|
2024
1860
|
if (depth === 1) return this.getServiceTypes(auth);
|
|
2025
1861
|
if (depth === 2) return this.getPlans(segments[1], path, auth);
|
|
2026
1862
|
if (depth === 3) return this.getPlanItems(segments[1], segments[2], auth);
|
|
2027
1863
|
return [];
|
|
2028
1864
|
}
|
|
2029
1865
|
async getServiceTypes(auth) {
|
|
2030
|
-
const response = await this.apiRequest(
|
|
2031
|
-
this.config.endpoints.serviceTypes,
|
|
2032
|
-
auth
|
|
2033
|
-
);
|
|
1866
|
+
const response = await this.apiRequest("/services/v2/service_types", auth);
|
|
2034
1867
|
if (!response?.data) return [];
|
|
2035
1868
|
return response.data.map((serviceType) => ({
|
|
2036
1869
|
type: "folder",
|
|
@@ -2040,9 +1873,8 @@ var PlanningCenterProvider = class {
|
|
|
2040
1873
|
}));
|
|
2041
1874
|
}
|
|
2042
1875
|
async getPlans(serviceTypeId, currentPath, auth) {
|
|
2043
|
-
const pathFn = this.config.endpoints.plans;
|
|
2044
1876
|
const response = await this.apiRequest(
|
|
2045
|
-
|
|
1877
|
+
`/services/v2/service_types/${serviceTypeId}/plans?filter=future&order=sort_date`,
|
|
2046
1878
|
auth
|
|
2047
1879
|
);
|
|
2048
1880
|
if (!response?.data) return [];
|
|
@@ -2050,7 +1882,7 @@ var PlanningCenterProvider = class {
|
|
|
2050
1882
|
const filteredPlans = response.data.filter((plan) => {
|
|
2051
1883
|
if (plan.attributes.items_count === 0) return false;
|
|
2052
1884
|
const planDate = new Date(plan.attributes.sort_date).getTime();
|
|
2053
|
-
return planDate < now +
|
|
1885
|
+
return planDate < now + ONE_WEEK_MS;
|
|
2054
1886
|
});
|
|
2055
1887
|
return filteredPlans.map((plan) => ({
|
|
2056
1888
|
type: "folder",
|
|
@@ -2061,17 +1893,13 @@ var PlanningCenterProvider = class {
|
|
|
2061
1893
|
}));
|
|
2062
1894
|
}
|
|
2063
1895
|
async getPlanItems(serviceTypeId, planId, auth) {
|
|
2064
|
-
const pathFn = this.config.endpoints.planItems;
|
|
2065
1896
|
const response = await this.apiRequest(
|
|
2066
|
-
|
|
1897
|
+
`/services/v2/service_types/${serviceTypeId}/plans/${planId}/items?per_page=100`,
|
|
2067
1898
|
auth
|
|
2068
1899
|
);
|
|
2069
1900
|
if (!response?.data) return [];
|
|
2070
1901
|
return response.data.map((item) => ({ type: "file", id: item.id, title: item.attributes.title || "", mediaType: "image", url: "" }));
|
|
2071
1902
|
}
|
|
2072
|
-
supportsDeviceFlow() {
|
|
2073
|
-
return false;
|
|
2074
|
-
}
|
|
2075
1903
|
};
|
|
2076
1904
|
|
|
2077
1905
|
// src/providers/bibleProject/data.json
|
|
@@ -4466,8 +4294,9 @@ var data_default = {
|
|
|
4466
4294
|
};
|
|
4467
4295
|
|
|
4468
4296
|
// src/providers/bibleProject/BibleProjectProvider.ts
|
|
4469
|
-
var BibleProjectProvider = class {
|
|
4297
|
+
var BibleProjectProvider = class extends BaseProvider {
|
|
4470
4298
|
constructor() {
|
|
4299
|
+
super(...arguments);
|
|
4471
4300
|
this.id = "bibleproject";
|
|
4472
4301
|
this.name = "The Bible Project";
|
|
4473
4302
|
this.logos = {
|
|
@@ -4480,10 +4309,8 @@ var BibleProjectProvider = class {
|
|
|
4480
4309
|
apiBase: "https://bibleproject.com",
|
|
4481
4310
|
oauthBase: "",
|
|
4482
4311
|
clientId: "",
|
|
4483
|
-
scopes: []
|
|
4484
|
-
endpoints: { downloads: "/downloads/" }
|
|
4312
|
+
scopes: []
|
|
4485
4313
|
};
|
|
4486
|
-
this.data = data_default;
|
|
4487
4314
|
this.requiresAuth = false;
|
|
4488
4315
|
this.authTypes = ["none"];
|
|
4489
4316
|
this.capabilities = {
|
|
@@ -4492,133 +4319,71 @@ var BibleProjectProvider = class {
|
|
|
4492
4319
|
instructions: true,
|
|
4493
4320
|
mediaLicensing: false
|
|
4494
4321
|
};
|
|
4322
|
+
this.data = data_default;
|
|
4495
4323
|
}
|
|
4496
4324
|
getMuxThumbnail(muxPlaybackId, width = 400, height = 225) {
|
|
4497
4325
|
return `https://image.mux.com/${muxPlaybackId}/thumbnail.jpg?width=${width}&height=${height}`;
|
|
4498
4326
|
}
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
return this.getCollections();
|
|
4503
|
-
}
|
|
4504
|
-
if (depth === 1) {
|
|
4505
|
-
const collectionSlug = segments[0];
|
|
4506
|
-
return this.getLessonFolders(collectionSlug, path);
|
|
4507
|
-
}
|
|
4508
|
-
if (depth === 2) {
|
|
4509
|
-
const collectionSlug = segments[0];
|
|
4510
|
-
const videoId = segments[1];
|
|
4511
|
-
return this.getVideoFile(collectionSlug, videoId);
|
|
4512
|
-
}
|
|
4513
|
-
return [];
|
|
4514
|
-
}
|
|
4515
|
-
getCollections() {
|
|
4516
|
-
return this.data.collections.filter((collection) => collection.videos.length > 0).map((collection) => ({
|
|
4517
|
-
type: "folder",
|
|
4518
|
-
id: slugify(collection.name),
|
|
4519
|
-
title: collection.name,
|
|
4520
|
-
thumbnail: collection.image || void 0,
|
|
4521
|
-
path: `/${slugify(collection.name)}`
|
|
4522
|
-
}));
|
|
4523
|
-
}
|
|
4524
|
-
getLessonFolders(collectionSlug, currentPath) {
|
|
4525
|
-
const collection = this.data.collections.find((c) => slugify(c.name) === collectionSlug);
|
|
4526
|
-
if (!collection) return [];
|
|
4527
|
-
return collection.videos.map((video) => ({
|
|
4528
|
-
type: "folder",
|
|
4529
|
-
id: video.id,
|
|
4530
|
-
title: video.title,
|
|
4327
|
+
videoToFile(video) {
|
|
4328
|
+
return createFile(video.id, video.title, video.videoUrl, {
|
|
4329
|
+
mediaType: "video",
|
|
4531
4330
|
thumbnail: this.getMuxThumbnail(video.muxPlaybackId),
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
})
|
|
4535
|
-
}
|
|
4536
|
-
getVideoFile(collectionSlug, videoId) {
|
|
4537
|
-
const collection = this.data.collections.find((c) => slugify(c.name) === collectionSlug);
|
|
4538
|
-
if (!collection) return [];
|
|
4539
|
-
const video = collection.videos.find((v) => v.id === videoId);
|
|
4540
|
-
if (!video) return [];
|
|
4541
|
-
return [createFile(video.id, video.title, video.videoUrl, { mediaType: "video", muxPlaybackId: video.muxPlaybackId, seconds: 0 })];
|
|
4331
|
+
muxPlaybackId: video.muxPlaybackId,
|
|
4332
|
+
seconds: 0
|
|
4333
|
+
});
|
|
4542
4334
|
}
|
|
4543
|
-
|
|
4335
|
+
/** Resolve a path to its collection and files: all videos at depth 1, a single video at depth 2. */
|
|
4336
|
+
resolve(path) {
|
|
4544
4337
|
const { segments, depth } = parsePath(path);
|
|
4545
|
-
if (depth < 1) return null;
|
|
4546
|
-
const
|
|
4547
|
-
const collection = this.data.collections.find((c) => slugify(c.name) === collectionSlug);
|
|
4338
|
+
if (depth < 1 || depth > 2) return null;
|
|
4339
|
+
const collection = this.data.collections.find((c) => slugify(c.name) === segments[0]);
|
|
4548
4340
|
if (!collection) return null;
|
|
4549
4341
|
if (depth === 1) {
|
|
4550
|
-
|
|
4551
|
-
return files.length > 0 ? files : null;
|
|
4342
|
+
return { collection, files: collection.videos.map((v) => this.videoToFile(v)), name: collection.name };
|
|
4552
4343
|
}
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
if (!video) return null;
|
|
4557
|
-
return [{ type: "file", id: video.id, title: video.title, mediaType: "video", url: video.videoUrl, thumbnail: this.getMuxThumbnail(video.muxPlaybackId), muxPlaybackId: video.muxPlaybackId, seconds: 0 }];
|
|
4558
|
-
}
|
|
4559
|
-
return null;
|
|
4344
|
+
const video = collection.videos.find((v) => v.id === segments[1]);
|
|
4345
|
+
if (!video) return null;
|
|
4346
|
+
return { collection, files: [this.videoToFile(video)], name: video.title };
|
|
4560
4347
|
}
|
|
4561
|
-
async
|
|
4348
|
+
async browse(path, _auth) {
|
|
4562
4349
|
const { segments, depth } = parsePath(path);
|
|
4563
|
-
if (depth
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4350
|
+
if (depth === 0) {
|
|
4351
|
+
return this.data.collections.filter((collection) => collection.videos.length > 0).map((collection) => ({
|
|
4352
|
+
type: "folder",
|
|
4353
|
+
id: slugify(collection.name),
|
|
4354
|
+
title: collection.name,
|
|
4355
|
+
thumbnail: collection.image || void 0,
|
|
4356
|
+
path: `/${slugify(collection.name)}`
|
|
4357
|
+
}));
|
|
4358
|
+
}
|
|
4567
4359
|
if (depth === 1) {
|
|
4568
|
-
const
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
label: video.title,
|
|
4578
|
-
downloadUrl: video.videoUrl,
|
|
4579
|
-
thumbnail: this.getMuxThumbnail(video.muxPlaybackId)
|
|
4580
|
-
}
|
|
4581
|
-
]
|
|
4360
|
+
const collection = this.data.collections.find((c) => slugify(c.name) === segments[0]);
|
|
4361
|
+
if (!collection) return [];
|
|
4362
|
+
return collection.videos.map((video) => ({
|
|
4363
|
+
type: "folder",
|
|
4364
|
+
id: video.id,
|
|
4365
|
+
title: video.title,
|
|
4366
|
+
thumbnail: this.getMuxThumbnail(video.muxPlaybackId),
|
|
4367
|
+
isLeaf: true,
|
|
4368
|
+
path: `${path}/${video.id}`
|
|
4582
4369
|
}));
|
|
4583
|
-
const sectionItem = {
|
|
4584
|
-
id: slugify(collection.name) + "-section",
|
|
4585
|
-
itemType: "section",
|
|
4586
|
-
label: collection.name,
|
|
4587
|
-
children: actionItems
|
|
4588
|
-
};
|
|
4589
|
-
return { name: collection.name, items: [sectionItem] };
|
|
4590
4370
|
}
|
|
4591
4371
|
if (depth === 2) {
|
|
4592
|
-
|
|
4593
|
-
const video = collection.videos.find((v) => v.id === videoId);
|
|
4594
|
-
if (!video) return null;
|
|
4595
|
-
const actionItem = {
|
|
4596
|
-
id: video.id + "-action",
|
|
4597
|
-
itemType: "action",
|
|
4598
|
-
label: video.title,
|
|
4599
|
-
actionType: "play",
|
|
4600
|
-
children: [
|
|
4601
|
-
{
|
|
4602
|
-
id: video.id,
|
|
4603
|
-
itemType: "file",
|
|
4604
|
-
label: video.title,
|
|
4605
|
-
downloadUrl: video.videoUrl,
|
|
4606
|
-
thumbnail: this.getMuxThumbnail(video.muxPlaybackId)
|
|
4607
|
-
}
|
|
4608
|
-
]
|
|
4609
|
-
};
|
|
4610
|
-
const sectionItem = {
|
|
4611
|
-
id: slugify(collection.name) + "-section",
|
|
4612
|
-
itemType: "section",
|
|
4613
|
-
label: collection.name,
|
|
4614
|
-
children: [actionItem]
|
|
4615
|
-
};
|
|
4616
|
-
return { name: video.title, items: [sectionItem] };
|
|
4372
|
+
return this.resolve(path)?.files ?? [];
|
|
4617
4373
|
}
|
|
4618
|
-
return
|
|
4374
|
+
return [];
|
|
4619
4375
|
}
|
|
4620
|
-
|
|
4621
|
-
|
|
4376
|
+
async getPlaylist(path, _auth, _resolution) {
|
|
4377
|
+
const resolved = this.resolve(path);
|
|
4378
|
+
return resolved && resolved.files.length > 0 ? resolved.files : null;
|
|
4379
|
+
}
|
|
4380
|
+
async getInstructions(path, _auth) {
|
|
4381
|
+
const resolved = this.resolve(path);
|
|
4382
|
+
if (!resolved) return null;
|
|
4383
|
+
return filesToInstructions(resolved.name, resolved.files, {
|
|
4384
|
+
id: slugify(resolved.collection.name) + "-section",
|
|
4385
|
+
label: resolved.collection.name
|
|
4386
|
+
});
|
|
4622
4387
|
}
|
|
4623
4388
|
};
|
|
4624
4389
|
|
|
@@ -12242,8 +12007,9 @@ function buildLessonInstructions(lesson) {
|
|
|
12242
12007
|
}
|
|
12243
12008
|
|
|
12244
12009
|
// src/providers/highVoltage/HighVoltageKidsProvider.ts
|
|
12245
|
-
var HighVoltageKidsProvider = class {
|
|
12010
|
+
var HighVoltageKidsProvider = class extends BaseProvider {
|
|
12246
12011
|
constructor() {
|
|
12012
|
+
super(...arguments);
|
|
12247
12013
|
this.id = "highvoltagekids";
|
|
12248
12014
|
this.name = "High Voltage Kids";
|
|
12249
12015
|
this.logos = {
|
|
@@ -12256,8 +12022,7 @@ var HighVoltageKidsProvider = class {
|
|
|
12256
12022
|
apiBase: "https://highvoltagekids.com",
|
|
12257
12023
|
oauthBase: "",
|
|
12258
12024
|
clientId: "",
|
|
12259
|
-
scopes: []
|
|
12260
|
-
endpoints: { downloads: "/membership-downloads/" }
|
|
12025
|
+
scopes: []
|
|
12261
12026
|
};
|
|
12262
12027
|
this.data = data_default2;
|
|
12263
12028
|
this.requiresAuth = false;
|
|
@@ -12307,9 +12072,6 @@ var HighVoltageKidsProvider = class {
|
|
|
12307
12072
|
}
|
|
12308
12073
|
return null;
|
|
12309
12074
|
}
|
|
12310
|
-
supportsDeviceFlow() {
|
|
12311
|
-
return false;
|
|
12312
|
-
}
|
|
12313
12075
|
};
|
|
12314
12076
|
|
|
12315
12077
|
// src/providers/jesusFilm/JesusFilmProvider.ts
|
|
@@ -12321,8 +12083,14 @@ var CATEGORY_MAP = {
|
|
|
12321
12083
|
"series": "series",
|
|
12322
12084
|
"collections": "collection"
|
|
12323
12085
|
};
|
|
12324
|
-
var
|
|
12086
|
+
var CATEGORY_NAMES = {
|
|
12087
|
+
"feature-films": "Feature Films",
|
|
12088
|
+
"series": "Series",
|
|
12089
|
+
"collections": "Collections"
|
|
12090
|
+
};
|
|
12091
|
+
var JesusFilmProvider = class extends BaseProvider {
|
|
12325
12092
|
constructor() {
|
|
12093
|
+
super(...arguments);
|
|
12326
12094
|
this.id = "jesusfilm";
|
|
12327
12095
|
this.name = "Jesus Film Project";
|
|
12328
12096
|
this.logos = {
|
|
@@ -12335,8 +12103,7 @@ var JesusFilmProvider = class {
|
|
|
12335
12103
|
apiBase: API_BASE4,
|
|
12336
12104
|
oauthBase: "",
|
|
12337
12105
|
clientId: "",
|
|
12338
|
-
scopes: []
|
|
12339
|
-
endpoints: { watch: "/watch" }
|
|
12106
|
+
scopes: []
|
|
12340
12107
|
};
|
|
12341
12108
|
this.requiresAuth = false;
|
|
12342
12109
|
this.authTypes = ["none"];
|
|
@@ -12347,6 +12114,7 @@ var JesusFilmProvider = class {
|
|
|
12347
12114
|
mediaLicensing: false
|
|
12348
12115
|
};
|
|
12349
12116
|
}
|
|
12117
|
+
// Not ApiHelper: Arclight needs the apiKey query param and callers rely on throw-on-error.
|
|
12350
12118
|
async fetchApi(path) {
|
|
12351
12119
|
const separator = path.includes("?") ? "&" : "?";
|
|
12352
12120
|
const url = `${API_BASE4}${path}${separator}apiKey=${API_KEY}`;
|
|
@@ -12354,6 +12122,12 @@ var JesusFilmProvider = class {
|
|
|
12354
12122
|
if (!response.ok) throw new Error(`Arclight API error: ${response.status}`);
|
|
12355
12123
|
return response.json();
|
|
12356
12124
|
}
|
|
12125
|
+
async fetchCategoryComponents(subType) {
|
|
12126
|
+
const data = await this.fetchApi(
|
|
12127
|
+
`/media-components?filter=master&subTypes=${subType}&languageIds=${LANGUAGE_ID}&limit=100`
|
|
12128
|
+
);
|
|
12129
|
+
return data._embedded.mediaComponents;
|
|
12130
|
+
}
|
|
12357
12131
|
async browse(path, _auth) {
|
|
12358
12132
|
const { segments, depth } = parsePath(path);
|
|
12359
12133
|
if (depth === 0) return this.getCategories();
|
|
@@ -12376,19 +12150,14 @@ var JesusFilmProvider = class {
|
|
|
12376
12150
|
async getItemsInCategory(category) {
|
|
12377
12151
|
const subType = CATEGORY_MAP[category];
|
|
12378
12152
|
if (!subType) return [];
|
|
12379
|
-
const
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
`/${category}/${item.mediaComponentId}`,
|
|
12388
|
-
item.imageUrls.mobileCinematicHigh || item.imageUrls.videoStill || item.imageUrls.thumbnail,
|
|
12389
|
-
isLeaf
|
|
12390
|
-
);
|
|
12391
|
-
});
|
|
12153
|
+
const components = await this.fetchCategoryComponents(subType);
|
|
12154
|
+
return components.map((item) => createFolder(
|
|
12155
|
+
item.mediaComponentId,
|
|
12156
|
+
item.title,
|
|
12157
|
+
`/${category}/${item.mediaComponentId}`,
|
|
12158
|
+
item.imageUrls.mobileCinematicHigh || item.imageUrls.videoStill || item.imageUrls.thumbnail,
|
|
12159
|
+
item.containsCount === 0
|
|
12160
|
+
));
|
|
12392
12161
|
}
|
|
12393
12162
|
async getVideoFile(mediaComponentId) {
|
|
12394
12163
|
const variant = await this.fetchApi(
|
|
@@ -12403,11 +12172,10 @@ var JesusFilmProvider = class {
|
|
|
12403
12172
|
const title = component?.title || mediaComponentId;
|
|
12404
12173
|
const seconds = component ? Math.round(component.lengthInMilliseconds / 1e3) : 0;
|
|
12405
12174
|
const thumbnail = component?.imageUrls.mobileCinematicHigh || component?.imageUrls.videoStill;
|
|
12406
|
-
const muxPlaybackId = this.extractMuxPlaybackId(downloadUrl);
|
|
12407
12175
|
return [
|
|
12408
12176
|
createFile(mediaComponentId, title, downloadUrl, {
|
|
12409
12177
|
mediaType: "video",
|
|
12410
|
-
muxPlaybackId,
|
|
12178
|
+
muxPlaybackId: this.extractMuxPlaybackId(downloadUrl),
|
|
12411
12179
|
seconds,
|
|
12412
12180
|
thumbnail
|
|
12413
12181
|
})
|
|
@@ -12419,9 +12187,8 @@ var JesusFilmProvider = class {
|
|
|
12419
12187
|
);
|
|
12420
12188
|
const childIds = linksData.linkedMediaComponentIds?.contains;
|
|
12421
12189
|
if (!childIds || childIds.length === 0) return [];
|
|
12422
|
-
const idsParam = childIds.join(",");
|
|
12423
12190
|
const childrenData = await this.fetchApi(
|
|
12424
|
-
`/media-components?ids=${
|
|
12191
|
+
`/media-components?ids=${childIds.join(",")}&languageIds=${LANGUAGE_ID}`
|
|
12425
12192
|
);
|
|
12426
12193
|
if (!childrenData._embedded?.mediaComponents) return [];
|
|
12427
12194
|
return childrenData._embedded.mediaComponents.map((item) => createFolder(
|
|
@@ -12436,36 +12203,6 @@ var JesusFilmProvider = class {
|
|
|
12436
12203
|
const match = url.match(/stream\.mux\.com\/([^/]+)/);
|
|
12437
12204
|
return match ? match[1] : void 0;
|
|
12438
12205
|
}
|
|
12439
|
-
async getPlaylist(path, _auth, _resolution) {
|
|
12440
|
-
const { segments, depth } = parsePath(path);
|
|
12441
|
-
if (depth < 1) return null;
|
|
12442
|
-
const category = segments[0];
|
|
12443
|
-
if (depth >= 3) {
|
|
12444
|
-
const lastId = segments[depth - 1];
|
|
12445
|
-
const items = await this.getVideoFile(lastId);
|
|
12446
|
-
const files2 = items.filter((item) => item.type === "file");
|
|
12447
|
-
if (files2.length > 0) return files2;
|
|
12448
|
-
const containerFiles = await this.fetchContainerVideoFiles(lastId);
|
|
12449
|
-
return containerFiles.length > 0 ? containerFiles : null;
|
|
12450
|
-
}
|
|
12451
|
-
if (depth === 2) {
|
|
12452
|
-
if (category === "feature-films") {
|
|
12453
|
-
const items = await this.getVideoFile(segments[1]);
|
|
12454
|
-
if (items.length === 0) return null;
|
|
12455
|
-
return items.filter((item) => item.type === "file");
|
|
12456
|
-
}
|
|
12457
|
-
const files2 = await this.fetchContainerVideoFiles(segments[1]);
|
|
12458
|
-
return files2.length > 0 ? files2 : null;
|
|
12459
|
-
}
|
|
12460
|
-
const subType = CATEGORY_MAP[category];
|
|
12461
|
-
if (!subType) return null;
|
|
12462
|
-
const data = await this.fetchApi(
|
|
12463
|
-
`/media-components?filter=master&subTypes=${subType}&languageIds=${LANGUAGE_ID}&limit=100`
|
|
12464
|
-
);
|
|
12465
|
-
const contentComponents = data._embedded.mediaComponents.filter((item) => item.containsCount === 0);
|
|
12466
|
-
const files = await this.fetchVideoFiles(contentComponents);
|
|
12467
|
-
return files.length > 0 ? files : null;
|
|
12468
|
-
}
|
|
12469
12206
|
async fetchVideoFiles(components) {
|
|
12470
12207
|
const files = [];
|
|
12471
12208
|
for (const component of components) {
|
|
@@ -12496,9 +12233,8 @@ var JesusFilmProvider = class {
|
|
|
12496
12233
|
);
|
|
12497
12234
|
const childIds = linksData.linkedMediaComponentIds?.contains;
|
|
12498
12235
|
if (!childIds || childIds.length === 0) return [];
|
|
12499
|
-
const idsParam = childIds.join(",");
|
|
12500
12236
|
const childrenData = await this.fetchApi(
|
|
12501
|
-
`/media-components?ids=${
|
|
12237
|
+
`/media-components?ids=${childIds.join(",")}&languageIds=${LANGUAGE_ID}`
|
|
12502
12238
|
);
|
|
12503
12239
|
if (!childrenData._embedded?.mediaComponents) return [];
|
|
12504
12240
|
const contentItems = childrenData._embedded.mediaComponents.filter((c) => c.containsCount === 0);
|
|
@@ -12510,64 +12246,47 @@ var JesusFilmProvider = class {
|
|
|
12510
12246
|
}
|
|
12511
12247
|
return files;
|
|
12512
12248
|
}
|
|
12249
|
+
async getPlaylist(path, _auth, _resolution) {
|
|
12250
|
+
const { segments, depth } = parsePath(path);
|
|
12251
|
+
if (depth < 1) return null;
|
|
12252
|
+
const category = segments[0];
|
|
12253
|
+
if (depth >= 3) {
|
|
12254
|
+
const lastId = segments[depth - 1];
|
|
12255
|
+
const items = await this.getVideoFile(lastId);
|
|
12256
|
+
const files2 = items.filter((item) => item.type === "file");
|
|
12257
|
+
if (files2.length > 0) return files2;
|
|
12258
|
+
const containerFiles = await this.fetchContainerVideoFiles(lastId);
|
|
12259
|
+
return containerFiles.length > 0 ? containerFiles : null;
|
|
12260
|
+
}
|
|
12261
|
+
if (depth === 2) {
|
|
12262
|
+
if (category === "feature-films") {
|
|
12263
|
+
const items = await this.getVideoFile(segments[1]);
|
|
12264
|
+
if (items.length === 0) return null;
|
|
12265
|
+
return items.filter((item) => item.type === "file");
|
|
12266
|
+
}
|
|
12267
|
+
const files2 = await this.fetchContainerVideoFiles(segments[1]);
|
|
12268
|
+
return files2.length > 0 ? files2 : null;
|
|
12269
|
+
}
|
|
12270
|
+
const subType = CATEGORY_MAP[category];
|
|
12271
|
+
if (!subType) return null;
|
|
12272
|
+
const components = await this.fetchCategoryComponents(subType);
|
|
12273
|
+
const files = await this.fetchVideoFiles(components.filter((item) => item.containsCount === 0));
|
|
12274
|
+
return files.length > 0 ? files : null;
|
|
12275
|
+
}
|
|
12513
12276
|
async buildVideoInstructions(mediaComponentId, category) {
|
|
12514
12277
|
const items = await this.getVideoFile(mediaComponentId);
|
|
12515
|
-
|
|
12516
|
-
|
|
12517
|
-
|
|
12518
|
-
const actionItem = {
|
|
12519
|
-
id: mediaComponentId + "-action",
|
|
12520
|
-
itemType: "action",
|
|
12521
|
-
label: file.title,
|
|
12522
|
-
actionType: "play",
|
|
12523
|
-
children: [
|
|
12524
|
-
{
|
|
12525
|
-
id: mediaComponentId,
|
|
12526
|
-
itemType: "file",
|
|
12527
|
-
label: file.title,
|
|
12528
|
-
downloadUrl: file.url,
|
|
12529
|
-
thumbnail: file.thumbnail
|
|
12530
|
-
}
|
|
12531
|
-
]
|
|
12532
|
-
};
|
|
12533
|
-
const sectionItem = {
|
|
12534
|
-
id: category + "-section",
|
|
12535
|
-
itemType: "section",
|
|
12536
|
-
label: file.title,
|
|
12537
|
-
children: [actionItem]
|
|
12538
|
-
};
|
|
12539
|
-
return { name: file.title, items: [sectionItem] };
|
|
12278
|
+
const file = items.find((item) => item.type === "file");
|
|
12279
|
+
if (!file) return null;
|
|
12280
|
+
return filesToInstructions(file.title, [file], { id: category + "-section", label: file.title });
|
|
12540
12281
|
}
|
|
12541
|
-
async buildContainerInstructions(containerId
|
|
12282
|
+
async buildContainerInstructions(containerId) {
|
|
12542
12283
|
const containerData = await this.fetchApi(
|
|
12543
12284
|
`/media-components?ids=${containerId}&languageIds=${LANGUAGE_ID}`
|
|
12544
12285
|
);
|
|
12545
|
-
const
|
|
12546
|
-
const containerName = container?.title || containerId;
|
|
12286
|
+
const containerName = containerData._embedded?.mediaComponents?.[0]?.title || containerId;
|
|
12547
12287
|
const videoFiles = await this.fetchContainerVideoFiles(containerId);
|
|
12548
12288
|
if (videoFiles.length === 0) return { name: containerName, items: [] };
|
|
12549
|
-
|
|
12550
|
-
id: file.id + "-action",
|
|
12551
|
-
itemType: "action",
|
|
12552
|
-
label: file.title,
|
|
12553
|
-
actionType: "play",
|
|
12554
|
-
children: [
|
|
12555
|
-
{
|
|
12556
|
-
id: file.id,
|
|
12557
|
-
itemType: "file",
|
|
12558
|
-
label: file.title,
|
|
12559
|
-
downloadUrl: file.url,
|
|
12560
|
-
thumbnail: file.thumbnail
|
|
12561
|
-
}
|
|
12562
|
-
]
|
|
12563
|
-
}));
|
|
12564
|
-
const sectionItem = {
|
|
12565
|
-
id: containerId + "-section",
|
|
12566
|
-
itemType: "section",
|
|
12567
|
-
label: containerName,
|
|
12568
|
-
children: actionItems
|
|
12569
|
-
};
|
|
12570
|
-
return { name: containerName, items: [sectionItem] };
|
|
12289
|
+
return filesToInstructions(containerName, videoFiles, { id: containerId + "-section", label: containerName });
|
|
12571
12290
|
}
|
|
12572
12291
|
async getInstructions(path, _auth) {
|
|
12573
12292
|
const { segments, depth } = parsePath(path);
|
|
@@ -12576,61 +12295,19 @@ var JesusFilmProvider = class {
|
|
|
12576
12295
|
const subType = CATEGORY_MAP[category];
|
|
12577
12296
|
if (!subType) return null;
|
|
12578
12297
|
if (depth === 1) {
|
|
12579
|
-
const
|
|
12580
|
-
|
|
12581
|
-
);
|
|
12582
|
-
|
|
12583
|
-
const actionItems = await Promise.all(
|
|
12584
|
-
data._embedded.mediaComponents.map(async (component) => {
|
|
12585
|
-
let downloadUrl;
|
|
12586
|
-
if (component.containsCount === 0) {
|
|
12587
|
-
try {
|
|
12588
|
-
const variant = await this.fetchApi(
|
|
12589
|
-
`/media-components/${component.mediaComponentId}/languages/${LANGUAGE_ID}?platform=web`
|
|
12590
|
-
);
|
|
12591
|
-
downloadUrl = variant.downloadUrls.high?.url || variant.downloadUrls.low?.url;
|
|
12592
|
-
} catch {
|
|
12593
|
-
}
|
|
12594
|
-
}
|
|
12595
|
-
return {
|
|
12596
|
-
id: component.mediaComponentId + "-action",
|
|
12597
|
-
itemType: "action",
|
|
12598
|
-
label: component.title,
|
|
12599
|
-
actionType: "play",
|
|
12600
|
-
children: downloadUrl ? [
|
|
12601
|
-
{
|
|
12602
|
-
id: component.mediaComponentId,
|
|
12603
|
-
itemType: "file",
|
|
12604
|
-
label: component.title,
|
|
12605
|
-
downloadUrl,
|
|
12606
|
-
thumbnail: component.imageUrls.mobileCinematicHigh || component.imageUrls.videoStill
|
|
12607
|
-
}
|
|
12608
|
-
] : []
|
|
12609
|
-
};
|
|
12610
|
-
})
|
|
12611
|
-
);
|
|
12612
|
-
const sectionItem = {
|
|
12613
|
-
id: category + "-section",
|
|
12614
|
-
itemType: "section",
|
|
12615
|
-
label: categoryName,
|
|
12616
|
-
children: actionItems.filter((a) => a.children && a.children.length > 0)
|
|
12617
|
-
};
|
|
12618
|
-
return { name: categoryName, items: [sectionItem] };
|
|
12298
|
+
const categoryName = CATEGORY_NAMES[category];
|
|
12299
|
+
const components = await this.fetchCategoryComponents(subType);
|
|
12300
|
+
const files = await this.fetchVideoFiles(components.filter((c) => c.containsCount === 0));
|
|
12301
|
+
return filesToInstructions(categoryName, files, { id: category + "-section", label: categoryName });
|
|
12619
12302
|
}
|
|
12620
12303
|
if (depth === 2) {
|
|
12621
12304
|
if (category === "feature-films") return this.buildVideoInstructions(segments[1], category);
|
|
12622
|
-
return this.buildContainerInstructions(segments[1]
|
|
12623
|
-
}
|
|
12624
|
-
if (depth >= 3) {
|
|
12625
|
-
const lastId = segments[depth - 1];
|
|
12626
|
-
const videoResult = await this.buildVideoInstructions(lastId, category);
|
|
12627
|
-
if (videoResult) return videoResult;
|
|
12628
|
-
return this.buildContainerInstructions(lastId, category);
|
|
12305
|
+
return this.buildContainerInstructions(segments[1]);
|
|
12629
12306
|
}
|
|
12630
|
-
|
|
12631
|
-
|
|
12632
|
-
|
|
12633
|
-
return
|
|
12307
|
+
const lastId = segments[depth - 1];
|
|
12308
|
+
const videoResult = await this.buildVideoInstructions(lastId, category);
|
|
12309
|
+
if (videoResult) return videoResult;
|
|
12310
|
+
return this.buildContainerInstructions(lastId);
|
|
12634
12311
|
}
|
|
12635
12312
|
};
|
|
12636
12313
|
|
|
@@ -12674,35 +12351,15 @@ function convertPlaylistToFiles(playlist) {
|
|
|
12674
12351
|
return file;
|
|
12675
12352
|
});
|
|
12676
12353
|
}
|
|
12677
|
-
function convertPlaylistToInstructions(playlist) {
|
|
12678
|
-
const files = convertPlaylistToFiles(playlist);
|
|
12679
|
-
const items = files.map((file) => ({
|
|
12680
|
-
id: file.id + "-action",
|
|
12681
|
-
itemType: "action",
|
|
12682
|
-
label: file.title,
|
|
12683
|
-
actionType: "play",
|
|
12684
|
-
children: [
|
|
12685
|
-
{
|
|
12686
|
-
id: file.id,
|
|
12687
|
-
itemType: "file",
|
|
12688
|
-
label: file.title,
|
|
12689
|
-
seconds: file.seconds,
|
|
12690
|
-
downloadUrl: file.url,
|
|
12691
|
-
thumbnail: file.thumbnail
|
|
12692
|
-
}
|
|
12693
|
-
]
|
|
12694
|
-
}));
|
|
12695
|
-
return { name: playlist.lesson_title || "Lesson", items };
|
|
12696
|
-
}
|
|
12697
12354
|
|
|
12698
12355
|
// src/providers/cbn/logo.ts
|
|
12699
12356
|
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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";
|
|
12700
12357
|
|
|
12701
12358
|
// src/providers/cbn/CbnProvider.ts
|
|
12702
12359
|
var API_BASE5 = "https://sbamemberdev.wpenginepowered.com/wp-json/superbook/v1";
|
|
12703
|
-
var CbnProvider = class {
|
|
12360
|
+
var CbnProvider = class extends BaseProvider {
|
|
12704
12361
|
constructor() {
|
|
12705
|
-
|
|
12362
|
+
super(...arguments);
|
|
12706
12363
|
this.deviceFlowHelper = new DeviceFlowHelper();
|
|
12707
12364
|
this.id = "cbn";
|
|
12708
12365
|
this.name = "CBN";
|
|
@@ -12715,66 +12372,48 @@ var CbnProvider = class {
|
|
|
12715
12372
|
clientId: "freeplay",
|
|
12716
12373
|
scopes: [],
|
|
12717
12374
|
supportsDeviceFlow: true,
|
|
12718
|
-
deviceAuthEndpoint: "/device"
|
|
12719
|
-
endpoints: {
|
|
12720
|
-
catalog: "/catalog",
|
|
12721
|
-
courseDetail: (courseId) => `/catalog/${courseId}`,
|
|
12722
|
-
lessonPlaylist: (lessonId) => `/lesson-playlist/${lessonId}`
|
|
12723
|
-
}
|
|
12375
|
+
deviceAuthEndpoint: "/device"
|
|
12724
12376
|
};
|
|
12725
12377
|
this.requiresAuth = true;
|
|
12726
12378
|
this.authTypes = ["device_flow"];
|
|
12727
12379
|
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
12728
12380
|
}
|
|
12729
|
-
async apiRequest(path, auth) {
|
|
12730
|
-
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
12731
|
-
}
|
|
12732
12381
|
async browse(path, auth) {
|
|
12733
12382
|
const { segments, depth } = parsePath(path);
|
|
12734
12383
|
if (depth === 0) {
|
|
12735
12384
|
return [{ type: "folder", id: "catalog-root", title: "Catalog", path: "/catalog" }];
|
|
12736
12385
|
}
|
|
12737
12386
|
if (segments[0] !== "catalog") return [];
|
|
12738
|
-
if (depth === 1)
|
|
12739
|
-
|
|
12740
|
-
|
|
12387
|
+
if (depth === 1) {
|
|
12388
|
+
const response = await this.apiRequest("/catalog", auth);
|
|
12389
|
+
return Array.isArray(response) ? convertCoursesToFolders(response) : [];
|
|
12390
|
+
}
|
|
12391
|
+
if (depth === 2) {
|
|
12392
|
+
const response = await this.apiRequest(`/catalog/${segments[1]}`, auth);
|
|
12393
|
+
return response?.lessons ? convertLessonsToFolders(response.lessons, path) : [];
|
|
12394
|
+
}
|
|
12395
|
+
if (depth === 3) {
|
|
12396
|
+
const resolved = await this.resolveFiles(path, auth);
|
|
12397
|
+
return resolved?.files ?? [];
|
|
12398
|
+
}
|
|
12741
12399
|
return [];
|
|
12742
12400
|
}
|
|
12743
|
-
|
|
12744
|
-
|
|
12745
|
-
if (!Array.isArray(response)) return [];
|
|
12746
|
-
return convertCoursesToFolders(response);
|
|
12747
|
-
}
|
|
12748
|
-
async getLessons(courseId, coursePath, auth) {
|
|
12749
|
-
const pathFn = this.config.endpoints.courseDetail;
|
|
12750
|
-
const response = await this.apiRequest(pathFn(courseId), auth);
|
|
12751
|
-
if (!response?.lessons) return [];
|
|
12752
|
-
return convertLessonsToFolders(response.lessons, coursePath);
|
|
12753
|
-
}
|
|
12754
|
-
async getVideos(lessonId, auth) {
|
|
12755
|
-
const playlist = await this.fetchPlaylist(lessonId, auth);
|
|
12756
|
-
return playlist ? convertPlaylistToFiles(playlist) : [];
|
|
12757
|
-
}
|
|
12758
|
-
async fetchPlaylist(lessonId, auth) {
|
|
12759
|
-
const pathFn = this.config.endpoints.lessonPlaylist;
|
|
12760
|
-
return this.apiRequest(pathFn(lessonId), auth);
|
|
12761
|
-
}
|
|
12762
|
-
async getPlaylist(path, auth, _resolution) {
|
|
12401
|
+
/** Resolve a leaf path (/catalog/{courseId}/{lessonId}) to its playlist files. */
|
|
12402
|
+
async resolveFiles(path, auth) {
|
|
12763
12403
|
const { segments, depth } = parsePath(path);
|
|
12764
12404
|
if (depth !== 3 || segments[0] !== "catalog") return null;
|
|
12765
|
-
const playlist = await this.
|
|
12405
|
+
const playlist = await this.apiRequest(`/lesson-playlist/${segments[2]}`, auth);
|
|
12766
12406
|
if (!playlist || playlist.playlist.length === 0) return null;
|
|
12767
|
-
return convertPlaylistToFiles(playlist);
|
|
12407
|
+
return { files: convertPlaylistToFiles(playlist), lessonTitle: playlist.lesson_title };
|
|
12768
12408
|
}
|
|
12769
|
-
async
|
|
12770
|
-
const
|
|
12771
|
-
|
|
12772
|
-
const playlist = await this.fetchPlaylist(segments[2], auth);
|
|
12773
|
-
if (!playlist || playlist.playlist.length === 0) return null;
|
|
12774
|
-
return convertPlaylistToInstructions(playlist);
|
|
12409
|
+
async getPlaylist(path, auth, _resolution) {
|
|
12410
|
+
const resolved = await this.resolveFiles(path, auth);
|
|
12411
|
+
return resolved?.files ?? null;
|
|
12775
12412
|
}
|
|
12776
|
-
|
|
12777
|
-
|
|
12413
|
+
async getInstructions(path, auth) {
|
|
12414
|
+
const resolved = await this.resolveFiles(path, auth);
|
|
12415
|
+
if (!resolved) return null;
|
|
12416
|
+
return filesToInstructions(resolved.lessonTitle || "Lesson", resolved.files);
|
|
12778
12417
|
}
|
|
12779
12418
|
async initiateDeviceFlow() {
|
|
12780
12419
|
return this.deviceFlowHelper.initiateDeviceFlow(this.config);
|
|
@@ -16405,8 +16044,9 @@ var data_default3 = {
|
|
|
16405
16044
|
};
|
|
16406
16045
|
|
|
16407
16046
|
// src/providers/lifeChurch/LifeChurchProvider.ts
|
|
16408
|
-
var LifeChurchProvider = class {
|
|
16047
|
+
var LifeChurchProvider = class extends BaseProvider {
|
|
16409
16048
|
constructor() {
|
|
16049
|
+
super(...arguments);
|
|
16410
16050
|
this.id = "lifechurch";
|
|
16411
16051
|
this.name = "Life.Church";
|
|
16412
16052
|
this.logos = {
|
|
@@ -16419,8 +16059,7 @@ var LifeChurchProvider = class {
|
|
|
16419
16059
|
apiBase: "https://www.life.church",
|
|
16420
16060
|
oauthBase: "",
|
|
16421
16061
|
clientId: "",
|
|
16422
|
-
scopes: []
|
|
16423
|
-
endpoints: {}
|
|
16062
|
+
scopes: []
|
|
16424
16063
|
};
|
|
16425
16064
|
this.requiresAuth = false;
|
|
16426
16065
|
this.authTypes = ["none"];
|
|
@@ -16435,20 +16074,14 @@ var LifeChurchProvider = class {
|
|
|
16435
16074
|
findSeries(seriesId) {
|
|
16436
16075
|
return this.data.series.find((s) => s.id === seriesId);
|
|
16437
16076
|
}
|
|
16438
|
-
findUnit(series, unitId) {
|
|
16439
|
-
return series.units.find((u) => u.id === unitId);
|
|
16440
|
-
}
|
|
16441
|
-
findLesson(unit, lessonId) {
|
|
16442
|
-
return unit.lessons.find((l) => l.id === lessonId);
|
|
16443
|
-
}
|
|
16444
16077
|
findScheduled(scheduledId) {
|
|
16445
16078
|
return this.data.scheduledLessons?.find((s) => s.id === scheduledId);
|
|
16446
16079
|
}
|
|
16447
16080
|
/** Resolve a scheduled-lesson reference to the underlying lesson + unit + series. */
|
|
16448
16081
|
resolveScheduled(sched) {
|
|
16449
16082
|
const series = this.findSeries(sched.seriesId);
|
|
16450
|
-
const unit = series
|
|
16451
|
-
const lesson = unit
|
|
16083
|
+
const unit = series?.units.find((u) => u.id === sched.unitId);
|
|
16084
|
+
const lesson = unit?.lessons.find((l) => l.id === sched.lessonId);
|
|
16452
16085
|
if (!series || !unit || !lesson) return null;
|
|
16453
16086
|
return { series, unit, lesson };
|
|
16454
16087
|
}
|
|
@@ -16463,17 +16096,40 @@ var LifeChurchProvider = class {
|
|
|
16463
16096
|
providerData: { sourceUrl: lesson.sourceUrl }
|
|
16464
16097
|
};
|
|
16465
16098
|
}
|
|
16099
|
+
/** Resolve a leaf-or-unit path to its files plus the name/section used for instructions. */
|
|
16100
|
+
resolve(path) {
|
|
16101
|
+
const { segments, depth } = parsePath(path);
|
|
16102
|
+
if (segments[0] === "scheduled") {
|
|
16103
|
+
if (depth !== 2) return null;
|
|
16104
|
+
const sched = this.findScheduled(segments[1]);
|
|
16105
|
+
const resolved = sched && this.resolveScheduled(sched);
|
|
16106
|
+
if (!sched || !resolved) return null;
|
|
16107
|
+
const label = `${sched.ageGroup} \u2014 ${resolved.lesson.title}`;
|
|
16108
|
+
return {
|
|
16109
|
+
files: [this.lessonToFile(resolved.lesson, label)],
|
|
16110
|
+
name: label,
|
|
16111
|
+
section: { id: `scheduled-${sched.id}-section`, label: "This Sunday" }
|
|
16112
|
+
};
|
|
16113
|
+
}
|
|
16114
|
+
const series = this.findSeries(segments[0]);
|
|
16115
|
+
const unit = series?.units.find((u) => u.id === segments[1]);
|
|
16116
|
+
if (!unit) return null;
|
|
16117
|
+
if (depth === 2) {
|
|
16118
|
+
return { files: unit.lessons.map((l) => this.lessonToFile(l)), name: unit.name, section: { id: `${unit.id}-section`, label: unit.name } };
|
|
16119
|
+
}
|
|
16120
|
+
if (depth === 3) {
|
|
16121
|
+
const lesson = unit.lessons.find((l) => l.id === segments[2]);
|
|
16122
|
+
if (!lesson) return null;
|
|
16123
|
+
return { files: [this.lessonToFile(lesson)], name: lesson.title, section: { id: `${unit.id}-section`, label: unit.name } };
|
|
16124
|
+
}
|
|
16125
|
+
return null;
|
|
16126
|
+
}
|
|
16466
16127
|
async browse(path, _auth) {
|
|
16467
16128
|
const { segments, depth } = parsePath(path);
|
|
16468
16129
|
if (depth === 0) {
|
|
16469
16130
|
const items = [];
|
|
16470
16131
|
if (this.data.scheduledLessons && this.data.scheduledLessons.length > 0) {
|
|
16471
|
-
items.push({
|
|
16472
|
-
type: "folder",
|
|
16473
|
-
id: "scheduled",
|
|
16474
|
-
title: "This Sunday",
|
|
16475
|
-
path: "/scheduled"
|
|
16476
|
-
});
|
|
16132
|
+
items.push({ type: "folder", id: "scheduled", title: "This Sunday", path: "/scheduled" });
|
|
16477
16133
|
}
|
|
16478
16134
|
for (const s of this.data.series) {
|
|
16479
16135
|
items.push({ type: "folder", id: s.id, title: s.name, thumbnail: s.thumbnail, path: `/${s.id}` });
|
|
@@ -16494,13 +16150,7 @@ var LifeChurchProvider = class {
|
|
|
16494
16150
|
};
|
|
16495
16151
|
});
|
|
16496
16152
|
}
|
|
16497
|
-
|
|
16498
|
-
const sched = this.findScheduled(segments[1]);
|
|
16499
|
-
const resolved = sched && this.resolveScheduled(sched);
|
|
16500
|
-
if (!sched || !resolved) return [];
|
|
16501
|
-
return [this.lessonToFile(resolved.lesson, `${sched.ageGroup} \u2014 ${resolved.lesson.title}`)];
|
|
16502
|
-
}
|
|
16503
|
-
return [];
|
|
16153
|
+
return this.resolve(path)?.files ?? [];
|
|
16504
16154
|
}
|
|
16505
16155
|
if (depth === 1) {
|
|
16506
16156
|
const series = this.findSeries(segments[0]);
|
|
@@ -16515,8 +16165,8 @@ var LifeChurchProvider = class {
|
|
|
16515
16165
|
}
|
|
16516
16166
|
if (depth === 2) {
|
|
16517
16167
|
const series = this.findSeries(segments[0]);
|
|
16518
|
-
const unit = series
|
|
16519
|
-
if (!
|
|
16168
|
+
const unit = series?.units.find((u) => u.id === segments[1]);
|
|
16169
|
+
if (!unit) return [];
|
|
16520
16170
|
return unit.lessons.map((l) => ({
|
|
16521
16171
|
type: "folder",
|
|
16522
16172
|
id: l.id,
|
|
@@ -16527,78 +16177,18 @@ var LifeChurchProvider = class {
|
|
|
16527
16177
|
}));
|
|
16528
16178
|
}
|
|
16529
16179
|
if (depth === 3) {
|
|
16530
|
-
|
|
16531
|
-
const unit = series && this.findUnit(series, segments[1]);
|
|
16532
|
-
const lesson = unit && this.findLesson(unit, segments[2]);
|
|
16533
|
-
if (!lesson) return [];
|
|
16534
|
-
return [this.lessonToFile(lesson)];
|
|
16180
|
+
return this.resolve(path)?.files ?? [];
|
|
16535
16181
|
}
|
|
16536
16182
|
return [];
|
|
16537
16183
|
}
|
|
16538
16184
|
async getPlaylist(path, _auth, _resolution) {
|
|
16539
|
-
const
|
|
16540
|
-
|
|
16541
|
-
const sched = this.findScheduled(segments[1]);
|
|
16542
|
-
const resolved = sched && this.resolveScheduled(sched);
|
|
16543
|
-
return resolved ? [this.lessonToFile(resolved.lesson, `${sched.ageGroup} \u2014 ${resolved.lesson.title}`)] : null;
|
|
16544
|
-
}
|
|
16545
|
-
if (depth === 3) {
|
|
16546
|
-
const series = this.findSeries(segments[0]);
|
|
16547
|
-
const unit = series && this.findUnit(series, segments[1]);
|
|
16548
|
-
const lesson = unit && this.findLesson(unit, segments[2]);
|
|
16549
|
-
return lesson ? [this.lessonToFile(lesson)] : null;
|
|
16550
|
-
}
|
|
16551
|
-
if (depth === 2 && segments[0] !== "scheduled") {
|
|
16552
|
-
const series = this.findSeries(segments[0]);
|
|
16553
|
-
const unit = series && this.findUnit(series, segments[1]);
|
|
16554
|
-
if (!unit) return null;
|
|
16555
|
-
const files = unit.lessons.map((l) => this.lessonToFile(l));
|
|
16556
|
-
return files.length > 0 ? files : null;
|
|
16557
|
-
}
|
|
16558
|
-
return null;
|
|
16185
|
+
const resolved = this.resolve(path);
|
|
16186
|
+
return resolved && resolved.files.length > 0 ? resolved.files : null;
|
|
16559
16187
|
}
|
|
16560
16188
|
async getInstructions(path, _auth) {
|
|
16561
|
-
const
|
|
16562
|
-
|
|
16563
|
-
|
|
16564
|
-
itemType: "action",
|
|
16565
|
-
label: label ?? lesson.title,
|
|
16566
|
-
actionType: "play",
|
|
16567
|
-
children: [
|
|
16568
|
-
{
|
|
16569
|
-
id: lesson.id,
|
|
16570
|
-
itemType: "file",
|
|
16571
|
-
label: label ?? lesson.title,
|
|
16572
|
-
downloadUrl: lesson.videoUrl,
|
|
16573
|
-
thumbnail: lesson.thumbnail,
|
|
16574
|
-
mediaType: "video"
|
|
16575
|
-
}
|
|
16576
|
-
]
|
|
16577
|
-
});
|
|
16578
|
-
if (segments[0] === "scheduled" && depth === 2) {
|
|
16579
|
-
const sched = this.findScheduled(segments[1]);
|
|
16580
|
-
const resolved = sched && this.resolveScheduled(sched);
|
|
16581
|
-
if (!sched || !resolved) return null;
|
|
16582
|
-
const label = `${sched.ageGroup} \u2014 ${resolved.lesson.title}`;
|
|
16583
|
-
return {
|
|
16584
|
-
name: label,
|
|
16585
|
-
items: [{ id: `scheduled-${sched.id}-section`, itemType: "section", label: "This Sunday", children: [buildItem(resolved.lesson, label)] }]
|
|
16586
|
-
};
|
|
16587
|
-
}
|
|
16588
|
-
if (depth === 3) {
|
|
16589
|
-
const series = this.findSeries(segments[0]);
|
|
16590
|
-
const unit = series && this.findUnit(series, segments[1]);
|
|
16591
|
-
const lesson = unit && this.findLesson(unit, segments[2]);
|
|
16592
|
-
if (!series || !unit || !lesson) return null;
|
|
16593
|
-
return { name: lesson.title, items: [{ id: `${unit.id}-section`, itemType: "section", label: unit.name, children: [buildItem(lesson)] }] };
|
|
16594
|
-
}
|
|
16595
|
-
if (depth === 2 && segments[0] !== "scheduled") {
|
|
16596
|
-
const series = this.findSeries(segments[0]);
|
|
16597
|
-
const unit = series && this.findUnit(series, segments[1]);
|
|
16598
|
-
if (!unit) return null;
|
|
16599
|
-
return { name: unit.name, items: [{ id: `${unit.id}-section`, itemType: "section", label: unit.name, children: unit.lessons.map((l) => buildItem(l)) }] };
|
|
16600
|
-
}
|
|
16601
|
-
return null;
|
|
16189
|
+
const resolved = this.resolve(path);
|
|
16190
|
+
if (!resolved) return null;
|
|
16191
|
+
return filesToInstructions(resolved.name, resolved.files, resolved.section);
|
|
16602
16192
|
}
|
|
16603
16193
|
/**
|
|
16604
16194
|
* Returns the most recent scheduled lesson from data.scheduledLessons.
|
|
@@ -16626,9 +16216,6 @@ var LifeChurchProvider = class {
|
|
|
16626
16216
|
]
|
|
16627
16217
|
};
|
|
16628
16218
|
}
|
|
16629
|
-
supportsDeviceFlow() {
|
|
16630
|
-
return false;
|
|
16631
|
-
}
|
|
16632
16219
|
};
|
|
16633
16220
|
|
|
16634
16221
|
// src/providers/index.ts
|
|
@@ -16723,17 +16310,20 @@ function getAvailableProviders(ids) {
|
|
|
16723
16310
|
}
|
|
16724
16311
|
|
|
16725
16312
|
// src/index.ts
|
|
16726
|
-
var VERSION = true ? "0.
|
|
16313
|
+
var VERSION = true ? "0.5.1" : "dev";
|
|
16727
16314
|
export {
|
|
16728
16315
|
APlayProvider,
|
|
16729
16316
|
B1ChurchProvider,
|
|
16317
|
+
BaseProvider,
|
|
16730
16318
|
BibleProjectProvider,
|
|
16731
16319
|
CbnProvider,
|
|
16320
|
+
DeviceFlowHelper,
|
|
16732
16321
|
DropboxProvider,
|
|
16733
16322
|
HighVoltageKidsProvider,
|
|
16734
16323
|
JesusFilmProvider,
|
|
16735
16324
|
LessonsChurchProvider,
|
|
16736
16325
|
LifeChurchProvider,
|
|
16326
|
+
OAuthHelper,
|
|
16737
16327
|
PlanningCenterProvider,
|
|
16738
16328
|
SignPresenterProvider,
|
|
16739
16329
|
TokenHelper,
|
|
@@ -16745,6 +16335,7 @@ export {
|
|
|
16745
16335
|
isContentFile,
|
|
16746
16336
|
isContentFolder,
|
|
16747
16337
|
navigateToPath,
|
|
16748
|
-
registerProvider
|
|
16338
|
+
registerProvider,
|
|
16339
|
+
toAuthData
|
|
16749
16340
|
};
|
|
16750
16341
|
//# sourceMappingURL=index.js.map
|