@churchapps/content-providers 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +94 -672
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -162
- package/dist/index.d.ts +49 -162
- package/dist/index.js +93 -659
- package/dist/index.js.map +1 -1
- package/package.json +57 -56
package/dist/index.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __export = (target, all) => {
|
|
3
|
-
for (var name in all)
|
|
4
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
-
};
|
|
6
|
-
|
|
7
1
|
// src/interfaces.ts
|
|
8
2
|
function isContentFolder(item) {
|
|
9
3
|
return item.type === "folder";
|
|
@@ -12,64 +6,6 @@ function isContentFile(item) {
|
|
|
12
6
|
return item.type === "file";
|
|
13
7
|
}
|
|
14
8
|
|
|
15
|
-
// src/utils.ts
|
|
16
|
-
function slugify(text) {
|
|
17
|
-
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
18
|
-
}
|
|
19
|
-
var VIDEO_EXTENSIONS = [".mp4", ".webm", ".m3u8", ".mov", ".avi", ".mkv", ".m4v"];
|
|
20
|
-
var IMAGE_EXTENSIONS = [
|
|
21
|
-
".jpg",
|
|
22
|
-
".jpeg",
|
|
23
|
-
".png",
|
|
24
|
-
".gif",
|
|
25
|
-
".bmp",
|
|
26
|
-
".svg",
|
|
27
|
-
".webp",
|
|
28
|
-
".tiff",
|
|
29
|
-
".tif"
|
|
30
|
-
];
|
|
31
|
-
function detectMediaType(url, explicitType) {
|
|
32
|
-
if (explicitType === "video" || explicitType?.startsWith("video/")) return "video";
|
|
33
|
-
if (explicitType === "image" || explicitType?.startsWith("image/")) return "image";
|
|
34
|
-
const lower = url.toLowerCase();
|
|
35
|
-
if (VIDEO_EXTENSIONS.some((p) => lower.includes(p)) || lower.includes("stream.mux.com")) return "video";
|
|
36
|
-
return "image";
|
|
37
|
-
}
|
|
38
|
-
function isMediaFile(filename) {
|
|
39
|
-
const lower = filename.toLowerCase();
|
|
40
|
-
return [...VIDEO_EXTENSIONS, ...IMAGE_EXTENSIONS].some((ext) => lower.endsWith(ext));
|
|
41
|
-
}
|
|
42
|
-
function createFolder(id, title, path, thumbnail, isLeaf) {
|
|
43
|
-
return { type: "folder", id, title, path, thumbnail, isLeaf };
|
|
44
|
-
}
|
|
45
|
-
function createFile(id, title, url, options) {
|
|
46
|
-
return { type: "file", id, title, url, mediaType: options?.mediaType ?? detectMediaType(url), thumbnail: options?.thumbnail, muxPlaybackId: options?.muxPlaybackId, seconds: options?.seconds, loop: options?.loop, loopVideo: options?.loopVideo, streamUrl: options?.streamUrl };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// src/pathUtils.ts
|
|
50
|
-
function parsePath(path) {
|
|
51
|
-
if (!path || path === "/" || path === "") {
|
|
52
|
-
return { segments: [], depth: 0 };
|
|
53
|
-
}
|
|
54
|
-
const segments = path.replace(/^\//, "").split("/").filter(Boolean);
|
|
55
|
-
return { segments, depth: segments.length };
|
|
56
|
-
}
|
|
57
|
-
function getSegment(path, index) {
|
|
58
|
-
const { segments } = parsePath(path);
|
|
59
|
-
return segments[index] ?? null;
|
|
60
|
-
}
|
|
61
|
-
function buildPath(segments) {
|
|
62
|
-
if (segments.length === 0) return "/";
|
|
63
|
-
return "/" + segments.join("/");
|
|
64
|
-
}
|
|
65
|
-
function appendToPath(basePath, segment) {
|
|
66
|
-
if (!basePath || basePath === "/" || basePath === "") {
|
|
67
|
-
return "/" + segment;
|
|
68
|
-
}
|
|
69
|
-
const cleanBase = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
70
|
-
return cleanBase + "/" + segment;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
9
|
// src/instructionPathUtils.ts
|
|
74
10
|
function navigateToPath(instructions, path) {
|
|
75
11
|
if (!path || !instructions?.items) return null;
|
|
@@ -81,283 +17,6 @@ function navigateToPath(instructions, path) {
|
|
|
81
17
|
}
|
|
82
18
|
return current;
|
|
83
19
|
}
|
|
84
|
-
function generatePath(indices) {
|
|
85
|
-
return indices.join(".");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// src/durationUtils.ts
|
|
89
|
-
var DEFAULT_DURATION_CONFIG = {
|
|
90
|
-
secondsPerImage: 15,
|
|
91
|
-
wordsPerMinute: 150
|
|
92
|
-
};
|
|
93
|
-
function countWords(text) {
|
|
94
|
-
if (!text || !text.trim()) return 0;
|
|
95
|
-
return text.trim().split(/\s+/).length;
|
|
96
|
-
}
|
|
97
|
-
function estimateImageDuration(config = {}) {
|
|
98
|
-
return config.secondsPerImage ?? DEFAULT_DURATION_CONFIG.secondsPerImage;
|
|
99
|
-
}
|
|
100
|
-
function estimateTextDuration(text, config = {}) {
|
|
101
|
-
const words = countWords(text);
|
|
102
|
-
const wpm = config.wordsPerMinute ?? DEFAULT_DURATION_CONFIG.wordsPerMinute;
|
|
103
|
-
return Math.ceil(words / wpm * 60);
|
|
104
|
-
}
|
|
105
|
-
function estimateDuration(mediaType, options) {
|
|
106
|
-
const config = options?.config ?? {};
|
|
107
|
-
switch (mediaType) {
|
|
108
|
-
case "image":
|
|
109
|
-
return estimateImageDuration(config);
|
|
110
|
-
case "text":
|
|
111
|
-
if (options?.wordCount) {
|
|
112
|
-
const wpm = config.wordsPerMinute ?? DEFAULT_DURATION_CONFIG.wordsPerMinute;
|
|
113
|
-
return Math.ceil(options.wordCount / wpm * 60);
|
|
114
|
-
}
|
|
115
|
-
if (options?.text) {
|
|
116
|
-
return estimateTextDuration(options.text, config);
|
|
117
|
-
}
|
|
118
|
-
return 0;
|
|
119
|
-
case "video":
|
|
120
|
-
default:
|
|
121
|
-
return 0;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// src/FormatConverters.ts
|
|
126
|
-
var FormatConverters_exports = {};
|
|
127
|
-
__export(FormatConverters_exports, {
|
|
128
|
-
expandedInstructionsToPlaylist: () => expandedInstructionsToPlaylist,
|
|
129
|
-
expandedInstructionsToPresentations: () => expandedInstructionsToPresentations,
|
|
130
|
-
instructionsToPlaylist: () => instructionsToPlaylist,
|
|
131
|
-
instructionsToPresentations: () => instructionsToPresentations,
|
|
132
|
-
playlistToExpandedInstructions: () => playlistToExpandedInstructions,
|
|
133
|
-
playlistToInstructions: () => playlistToInstructions,
|
|
134
|
-
playlistToPresentations: () => playlistToPresentations,
|
|
135
|
-
presentationsToExpandedInstructions: () => presentationsToExpandedInstructions,
|
|
136
|
-
presentationsToPlaylist: () => presentationsToPlaylist
|
|
137
|
-
});
|
|
138
|
-
function generateId() {
|
|
139
|
-
return "gen-" + Math.random().toString(36).substring(2, 11);
|
|
140
|
-
}
|
|
141
|
-
function mapItemTypeToActionType(itemType) {
|
|
142
|
-
switch (itemType) {
|
|
143
|
-
case "action":
|
|
144
|
-
case "lessonAction":
|
|
145
|
-
case "providerPresentation":
|
|
146
|
-
case "play":
|
|
147
|
-
case "addon":
|
|
148
|
-
case "add-on":
|
|
149
|
-
case "lessonAddOn":
|
|
150
|
-
case "providerFile":
|
|
151
|
-
return "play";
|
|
152
|
-
default:
|
|
153
|
-
return "other";
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
function presentationsToPlaylist(plan) {
|
|
157
|
-
if (plan.allFiles && plan.allFiles.length > 0) {
|
|
158
|
-
return [...plan.allFiles];
|
|
159
|
-
}
|
|
160
|
-
const files = [];
|
|
161
|
-
for (const section of plan.sections) {
|
|
162
|
-
for (const presentation of section.presentations) {
|
|
163
|
-
files.push(...presentation.files);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return files;
|
|
167
|
-
}
|
|
168
|
-
function mapActionTypeToItemType(actionType) {
|
|
169
|
-
switch (actionType) {
|
|
170
|
-
case "play":
|
|
171
|
-
return "action";
|
|
172
|
-
default:
|
|
173
|
-
return "item";
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
function presentationsToExpandedInstructions(plan) {
|
|
177
|
-
return {
|
|
178
|
-
name: plan.name,
|
|
179
|
-
items: plan.sections.map((section) => ({
|
|
180
|
-
id: section.id,
|
|
181
|
-
itemType: "section",
|
|
182
|
-
label: section.name,
|
|
183
|
-
children: section.presentations.map((pres) => ({
|
|
184
|
-
id: pres.id,
|
|
185
|
-
itemType: mapActionTypeToItemType(pres.actionType),
|
|
186
|
-
label: pres.name,
|
|
187
|
-
description: pres.actionType !== "other" ? pres.actionType : void 0,
|
|
188
|
-
seconds: pres.files.reduce((sum, f) => sum + (f.seconds || 0), 0) || void 0,
|
|
189
|
-
children: pres.files.map((f) => ({
|
|
190
|
-
id: f.id,
|
|
191
|
-
itemType: "file",
|
|
192
|
-
label: f.title,
|
|
193
|
-
seconds: f.seconds,
|
|
194
|
-
downloadUrl: f.downloadUrl || f.url,
|
|
195
|
-
thumbnail: f.thumbnail
|
|
196
|
-
}))
|
|
197
|
-
}))
|
|
198
|
-
}))
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
function instructionsToPlaylist(instructions) {
|
|
202
|
-
const files = [];
|
|
203
|
-
function extractFiles(items) {
|
|
204
|
-
for (const item of items) {
|
|
205
|
-
if (item.downloadUrl && (item.itemType === "file" || !item.children?.length)) {
|
|
206
|
-
files.push({ type: "file", id: item.id || item.relatedId || generateId(), title: item.label || "Untitled", mediaType: detectMediaType(item.downloadUrl), url: item.downloadUrl, downloadUrl: item.downloadUrl, seconds: item.seconds, thumbnail: item.thumbnail });
|
|
207
|
-
}
|
|
208
|
-
if (item.children) {
|
|
209
|
-
extractFiles(item.children);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
extractFiles(instructions.items);
|
|
214
|
-
return files;
|
|
215
|
-
}
|
|
216
|
-
var expandedInstructionsToPlaylist = instructionsToPlaylist;
|
|
217
|
-
function instructionsToPresentations(instructions, planId) {
|
|
218
|
-
const allFiles = [];
|
|
219
|
-
const sections = instructions.items.filter((item) => item.children && item.children.length > 0).map((sectionItem) => {
|
|
220
|
-
const presentations = (sectionItem.children || []).map((presItem) => {
|
|
221
|
-
const files = [];
|
|
222
|
-
if (presItem.children && presItem.children.length > 0) {
|
|
223
|
-
for (const child of presItem.children) {
|
|
224
|
-
if (child.downloadUrl) {
|
|
225
|
-
const file = { type: "file", id: child.id || child.relatedId || generateId(), title: child.label || "Untitled", mediaType: detectMediaType(child.downloadUrl), url: child.downloadUrl, downloadUrl: child.downloadUrl, seconds: child.seconds, thumbnail: child.thumbnail };
|
|
226
|
-
allFiles.push(file);
|
|
227
|
-
files.push(file);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
if (files.length === 0 && presItem.downloadUrl) {
|
|
232
|
-
const file = { type: "file", id: presItem.id || presItem.relatedId || generateId(), title: presItem.label || "Untitled", mediaType: detectMediaType(presItem.downloadUrl), url: presItem.downloadUrl, downloadUrl: presItem.downloadUrl, seconds: presItem.seconds, thumbnail: presItem.thumbnail };
|
|
233
|
-
allFiles.push(file);
|
|
234
|
-
files.push(file);
|
|
235
|
-
}
|
|
236
|
-
return { id: presItem.id || presItem.relatedId || generateId(), name: presItem.label || "Presentation", actionType: mapItemTypeToActionType(presItem.itemType), files };
|
|
237
|
-
});
|
|
238
|
-
return { id: sectionItem.id || sectionItem.relatedId || generateId(), name: sectionItem.label || "Section", presentations };
|
|
239
|
-
});
|
|
240
|
-
return { id: planId || generateId(), name: instructions.name || "Plan", sections, allFiles };
|
|
241
|
-
}
|
|
242
|
-
var expandedInstructionsToPresentations = instructionsToPresentations;
|
|
243
|
-
function playlistToPresentations(files, planName = "Playlist", sectionName = "Content") {
|
|
244
|
-
const presentations = files.map((file, index) => ({ id: `pres-${index}-${file.id}`, name: file.title, actionType: "play", files: [file] }));
|
|
245
|
-
return { id: "playlist-plan-" + generateId(), name: planName, sections: [{ id: "main-section", name: sectionName, presentations }], allFiles: [...files] };
|
|
246
|
-
}
|
|
247
|
-
function playlistToInstructions(files, name = "Playlist") {
|
|
248
|
-
return { name, items: [{ id: "main-section", itemType: "section", label: "Content", children: files.map((file, index) => ({ id: file.id || `item-${index}`, itemType: "file", label: file.title, seconds: file.seconds, downloadUrl: file.downloadUrl || file.url, thumbnail: file.thumbnail })) }] };
|
|
249
|
-
}
|
|
250
|
-
var playlistToExpandedInstructions = playlistToInstructions;
|
|
251
|
-
|
|
252
|
-
// src/FormatResolver.ts
|
|
253
|
-
var FormatResolver = class {
|
|
254
|
-
constructor(provider, options = {}) {
|
|
255
|
-
this.provider = provider;
|
|
256
|
-
this.options = { allowLossy: options.allowLossy ?? true };
|
|
257
|
-
}
|
|
258
|
-
getProvider() {
|
|
259
|
-
return this.provider;
|
|
260
|
-
}
|
|
261
|
-
/** Extract the last segment from a path to use as fallback ID/title */
|
|
262
|
-
getIdFromPath(path) {
|
|
263
|
-
const { segments } = parsePath(path);
|
|
264
|
-
return segments[segments.length - 1] || "content";
|
|
265
|
-
}
|
|
266
|
-
async getPlaylist(path, auth) {
|
|
267
|
-
const caps = this.provider.capabilities;
|
|
268
|
-
if (caps.playlist && this.provider.getPlaylist) {
|
|
269
|
-
const result = await this.provider.getPlaylist(path, auth);
|
|
270
|
-
if (result && result.length > 0) return result;
|
|
271
|
-
}
|
|
272
|
-
if (caps.instructions && this.provider.getInstructions) {
|
|
273
|
-
const expanded = await this.provider.getInstructions(path, auth);
|
|
274
|
-
if (expanded) return instructionsToPlaylist(expanded);
|
|
275
|
-
}
|
|
276
|
-
return null;
|
|
277
|
-
}
|
|
278
|
-
async getPlaylistWithMeta(path, auth) {
|
|
279
|
-
const caps = this.provider.capabilities;
|
|
280
|
-
if (caps.playlist && this.provider.getPlaylist) {
|
|
281
|
-
const result = await this.provider.getPlaylist(path, auth);
|
|
282
|
-
if (result && result.length > 0) {
|
|
283
|
-
return { data: result, meta: { isNative: true, isLossy: false } };
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
if (caps.instructions && this.provider.getInstructions) {
|
|
287
|
-
const expanded = await this.provider.getInstructions(path, auth);
|
|
288
|
-
if (expanded) return { data: instructionsToPlaylist(expanded), meta: { isNative: false, sourceFormat: "instructions", isLossy: false } };
|
|
289
|
-
}
|
|
290
|
-
return { data: null, meta: { isNative: false, isLossy: false } };
|
|
291
|
-
}
|
|
292
|
-
// async getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
293
|
-
// const caps = this.provider.capabilities;
|
|
294
|
-
// const fallbackId = this.getIdFromPath(path);
|
|
295
|
-
// if (caps.presentations) {
|
|
296
|
-
// const result = await this.provider.getPresentations(path, auth);
|
|
297
|
-
// if (result) return result;
|
|
298
|
-
// }
|
|
299
|
-
// if (caps.instructions && this.provider.getInstructions) {
|
|
300
|
-
// const expanded = await this.provider.getInstructions(path, auth);
|
|
301
|
-
// if (expanded) return Converters.instructionsToPresentations(expanded, fallbackId);
|
|
302
|
-
// }
|
|
303
|
-
// if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
304
|
-
// const playlist = await this.provider.getPlaylist(path, auth);
|
|
305
|
-
// if (playlist && playlist.length > 0) {
|
|
306
|
-
// return Converters.playlistToPresentations(playlist, fallbackId);
|
|
307
|
-
// }
|
|
308
|
-
// }
|
|
309
|
-
// return null;
|
|
310
|
-
// }
|
|
311
|
-
// async getPresentationsWithMeta(path: string, auth?: ContentProviderAuthData | null): Promise<{ data: Plan | null; meta: ResolvedFormatMeta }> {
|
|
312
|
-
// const caps = this.provider.capabilities;
|
|
313
|
-
// const fallbackId = this.getIdFromPath(path);
|
|
314
|
-
// if (caps.presentations) {
|
|
315
|
-
// const result = await this.provider.getPresentations(path, auth);
|
|
316
|
-
// if (result) {
|
|
317
|
-
// return { data: result, meta: { isNative: true, isLossy: false } };
|
|
318
|
-
// }
|
|
319
|
-
// }
|
|
320
|
-
// if (caps.instructions && this.provider.getInstructions) {
|
|
321
|
-
// const expanded = await this.provider.getInstructions(path, auth);
|
|
322
|
-
// if (expanded) return { data: Converters.instructionsToPresentations(expanded, fallbackId), meta: { isNative: false, sourceFormat: "instructions", isLossy: false } };
|
|
323
|
-
// }
|
|
324
|
-
// if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
325
|
-
// const playlist = await this.provider.getPlaylist(path, auth);
|
|
326
|
-
// if (playlist && playlist.length > 0) return { data: Converters.playlistToPresentations(playlist, fallbackId), meta: { isNative: false, sourceFormat: "playlist", isLossy: true } };
|
|
327
|
-
// }
|
|
328
|
-
// return { data: null, meta: { isNative: false, isLossy: false } };
|
|
329
|
-
// }
|
|
330
|
-
async getInstructions(path, auth) {
|
|
331
|
-
const caps = this.provider.capabilities;
|
|
332
|
-
const fallbackTitle = this.getIdFromPath(path);
|
|
333
|
-
if (caps.instructions && this.provider.getInstructions) {
|
|
334
|
-
const result = await this.provider.getInstructions(path, auth);
|
|
335
|
-
if (result) return result;
|
|
336
|
-
}
|
|
337
|
-
if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
338
|
-
const playlist = await this.provider.getPlaylist(path, auth);
|
|
339
|
-
if (playlist && playlist.length > 0) {
|
|
340
|
-
return playlistToInstructions(playlist, fallbackTitle);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return null;
|
|
344
|
-
}
|
|
345
|
-
async getInstructionsWithMeta(path, auth) {
|
|
346
|
-
const caps = this.provider.capabilities;
|
|
347
|
-
const fallbackTitle = this.getIdFromPath(path);
|
|
348
|
-
if (caps.instructions && this.provider.getInstructions) {
|
|
349
|
-
const result = await this.provider.getInstructions(path, auth);
|
|
350
|
-
if (result) {
|
|
351
|
-
return { data: result, meta: { isNative: true, isLossy: false } };
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
355
|
-
const playlist = await this.provider.getPlaylist(path, auth);
|
|
356
|
-
if (playlist && playlist.length > 0) return { data: playlistToInstructions(playlist, fallbackTitle), meta: { isNative: false, sourceFormat: "playlist", isLossy: true } };
|
|
357
|
-
}
|
|
358
|
-
return { data: null, meta: { isNative: false, isLossy: false } };
|
|
359
|
-
}
|
|
360
|
-
};
|
|
361
20
|
|
|
362
21
|
// src/helpers/OAuthHelper.ts
|
|
363
22
|
var OAuthHelper = class {
|
|
@@ -521,6 +180,19 @@ var ApiHelper = class {
|
|
|
521
180
|
}
|
|
522
181
|
};
|
|
523
182
|
|
|
183
|
+
// src/pathUtils.ts
|
|
184
|
+
function parsePath(path) {
|
|
185
|
+
if (!path || path === "/" || path === "") {
|
|
186
|
+
return { segments: [], depth: 0 };
|
|
187
|
+
}
|
|
188
|
+
const segments = path.replace(/^\//, "").split("/").filter(Boolean);
|
|
189
|
+
return { segments, depth: segments.length };
|
|
190
|
+
}
|
|
191
|
+
function getSegment(path, index) {
|
|
192
|
+
const { segments } = parsePath(path);
|
|
193
|
+
return segments[index] ?? null;
|
|
194
|
+
}
|
|
195
|
+
|
|
524
196
|
// src/providers/aPlay/APlayApi.ts
|
|
525
197
|
var API_BASE = "https://api-prod.amazingkids.app";
|
|
526
198
|
async function checkMediaLicense(mediaId, auth) {
|
|
@@ -541,6 +213,57 @@ async function checkMediaLicense(mediaId, auth) {
|
|
|
541
213
|
}
|
|
542
214
|
}
|
|
543
215
|
|
|
216
|
+
// src/utils.ts
|
|
217
|
+
var IMAGE_DURATION_SECONDS = 15;
|
|
218
|
+
function slugify(text) {
|
|
219
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
220
|
+
}
|
|
221
|
+
var VIDEO_EXTENSIONS = [".mp4", ".webm", ".m3u8", ".mov", ".avi", ".mkv", ".m4v"];
|
|
222
|
+
var IMAGE_EXTENSIONS = [
|
|
223
|
+
".jpg",
|
|
224
|
+
".jpeg",
|
|
225
|
+
".png",
|
|
226
|
+
".gif",
|
|
227
|
+
".bmp",
|
|
228
|
+
".svg",
|
|
229
|
+
".webp",
|
|
230
|
+
".tiff",
|
|
231
|
+
".tif"
|
|
232
|
+
];
|
|
233
|
+
function detectMediaType(url, explicitType) {
|
|
234
|
+
if (explicitType === "video" || explicitType?.startsWith("video/")) return "video";
|
|
235
|
+
if (explicitType === "image" || explicitType?.startsWith("image/")) return "image";
|
|
236
|
+
const lower = url.toLowerCase();
|
|
237
|
+
if (VIDEO_EXTENSIONS.some((p) => lower.includes(p)) || lower.includes("stream.mux.com")) return "video";
|
|
238
|
+
return "image";
|
|
239
|
+
}
|
|
240
|
+
function isMediaFile(filename) {
|
|
241
|
+
const lower = filename.toLowerCase();
|
|
242
|
+
return [...VIDEO_EXTENSIONS, ...IMAGE_EXTENSIONS].some((ext) => lower.endsWith(ext));
|
|
243
|
+
}
|
|
244
|
+
function createFolder(id, title, path, thumbnail, isLeaf) {
|
|
245
|
+
return { type: "folder", id, title, path, thumbnail, isLeaf };
|
|
246
|
+
}
|
|
247
|
+
function createFile(id, title, url, options) {
|
|
248
|
+
return { type: "file", id, title, url, mediaType: options?.mediaType ?? detectMediaType(url), thumbnail: options?.thumbnail, muxPlaybackId: options?.muxPlaybackId, seconds: options?.seconds, loop: options?.loop, loopVideo: options?.loopVideo, streamUrl: options?.streamUrl };
|
|
249
|
+
}
|
|
250
|
+
function generateId() {
|
|
251
|
+
return "gen-" + Math.random().toString(36).substring(2, 11);
|
|
252
|
+
}
|
|
253
|
+
function instructionsToPlaylist(instructions) {
|
|
254
|
+
const files = [];
|
|
255
|
+
function extractFiles(items) {
|
|
256
|
+
for (const item of items) {
|
|
257
|
+
if (item.downloadUrl && (item.itemType === "file" || !item.children?.length)) {
|
|
258
|
+
files.push({ type: "file", id: item.id || item.relatedId || generateId(), title: item.label || "Untitled", mediaType: detectMediaType(item.downloadUrl), url: item.downloadUrl, downloadUrl: item.downloadUrl, seconds: item.seconds, thumbnail: item.thumbnail });
|
|
259
|
+
}
|
|
260
|
+
if (item.children) extractFiles(item.children);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
extractFiles(instructions.items);
|
|
264
|
+
return files;
|
|
265
|
+
}
|
|
266
|
+
|
|
544
267
|
// src/providers/aPlay/APlayConverters.ts
|
|
545
268
|
function extractLibraryId(path) {
|
|
546
269
|
const { segments, depth } = parsePath(path);
|
|
@@ -647,7 +370,7 @@ var APlayProvider = class {
|
|
|
647
370
|
this.config = { id: "aplay", name: "APlay", apiBase: API_BASE, oauthBase: "https://api.joinamazing.com/prod/aims/oauth", clientId: "xFJFq7yNYuXXXMx0YBiQ", scopes: ["openid", "profile", "email"], endpoints: { modules: "/prod/curriculum/modules", productLibraries: (productId) => `/prod/curriculum/modules/products/${productId}/libraries`, libraryMedia: (libraryId) => `/prod/creators/libraries/${libraryId}/media` } };
|
|
648
371
|
this.requiresAuth = true;
|
|
649
372
|
this.authTypes = ["oauth_pkce"];
|
|
650
|
-
this.capabilities = { browse: true,
|
|
373
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: true };
|
|
651
374
|
}
|
|
652
375
|
async apiRequest(path, auth) {
|
|
653
376
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
@@ -699,13 +422,6 @@ var APlayProvider = class {
|
|
|
699
422
|
const mediaItems = extractArray(response, "data", "media");
|
|
700
423
|
return convertMediaToFiles(mediaItems);
|
|
701
424
|
}
|
|
702
|
-
// async getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
703
|
-
// const libraryId = extractLibraryId(path);
|
|
704
|
-
// if (!libraryId) return null;
|
|
705
|
-
// const files = await this.getMediaFiles(libraryId, auth) as ContentFile[];
|
|
706
|
-
// if (files.length === 0) return null;
|
|
707
|
-
// return convertFilesToPresentations(files, libraryId).plan;
|
|
708
|
-
// }
|
|
709
425
|
async getPlaylist(path, auth, _resolution) {
|
|
710
426
|
const libraryId = extractLibraryId(path);
|
|
711
427
|
if (!libraryId) return null;
|
|
@@ -739,7 +455,7 @@ var SignPresenterProvider = class {
|
|
|
739
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", endpoints: { playlists: "/content/playlists", messages: (playlistId) => `/content/playlists/${playlistId}/messages` } };
|
|
740
456
|
this.requiresAuth = true;
|
|
741
457
|
this.authTypes = ["oauth_pkce", "device_flow"];
|
|
742
|
-
this.capabilities = { browse: true,
|
|
458
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
743
459
|
}
|
|
744
460
|
async apiRequest(path, auth) {
|
|
745
461
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
@@ -808,20 +524,6 @@ var SignPresenterProvider = class {
|
|
|
808
524
|
}
|
|
809
525
|
return files;
|
|
810
526
|
}
|
|
811
|
-
// async getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
812
|
-
// const { segments, depth } = parsePath(path);
|
|
813
|
-
// if (depth < 2 || segments[0] !== "playlists") return null;
|
|
814
|
-
// const playlistId = segments[1];
|
|
815
|
-
// const files = await this.getMessages(playlistId, auth) as ContentFile[];
|
|
816
|
-
// if (files.length === 0) return null;
|
|
817
|
-
// // Get playlist info for title
|
|
818
|
-
// const playlists = await this.getPlaylists(auth);
|
|
819
|
-
// const playlist = playlists.find(p => p.id === playlistId);
|
|
820
|
-
// const title = playlist?.title || "Playlist";
|
|
821
|
-
// const thumbnail = (playlist as Record<string, unknown> | undefined)?.image as string | undefined;
|
|
822
|
-
// const presentations: PlanPresentation[] = files.map(f => ({ id: f.id, name: f.title, actionType: "play" as const, files: [f] }));
|
|
823
|
-
// return { id: playlistId, name: title as string, thumbnail, sections: [{ id: `section-${playlistId}`, name: title as string, presentations }], allFiles: files };
|
|
824
|
-
// }
|
|
825
527
|
async getPlaylist(path, auth, _resolution) {
|
|
826
528
|
const { segments, depth } = parsePath(path);
|
|
827
529
|
if (depth < 2 || segments[0] !== "playlists") return null;
|
|
@@ -967,7 +669,7 @@ function buildSectionActionsMap(actionsResponse, lessonImage, feedResponse) {
|
|
|
967
669
|
for (const section of actionsResponse.sections) {
|
|
968
670
|
if (section.id && section.actions) {
|
|
969
671
|
sectionActionsMap.set(section.id, section.actions.map((action) => {
|
|
970
|
-
const seconds = action.seconds ??
|
|
672
|
+
const seconds = action.seconds ?? IMAGE_DURATION_SECONDS;
|
|
971
673
|
const rawActionType = action.actionType?.toLowerCase() || "";
|
|
972
674
|
const hasFiles = rawActionType === "play" || rawActionType === "add-on";
|
|
973
675
|
const thumbnail = action.id && actionThumbnailMap.get(action.id) || lessonImage;
|
|
@@ -1073,7 +775,7 @@ var LessonsChurchProvider = class {
|
|
|
1073
775
|
this.config = { id: "lessonschurch", name: "Lessons.church", apiBase: API_BASE2, oauthBase: "", clientId: "", scopes: [], endpoints: { programs: "/programs/public", studies: (programId) => `/studies/public/program/${programId}`, lessons: (studyId) => `/lessons/public/study/${studyId}`, venues: (lessonId) => `/venues/public/lesson/${lessonId}`, playlist: (venueId) => `/venues/playlist/${venueId}`, feed: (venueId) => `/venues/public/feed/${venueId}`, addOns: "/addOns/public", addOnDetail: (id) => `/addOns/public/${id}` } };
|
|
1074
776
|
this.requiresAuth = false;
|
|
1075
777
|
this.authTypes = ["none"];
|
|
1076
|
-
this.capabilities = { browse: true,
|
|
778
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
1077
779
|
}
|
|
1078
780
|
async getPlaylist(path, _auth, resolution) {
|
|
1079
781
|
const { segments } = parsePath(path);
|
|
@@ -1184,19 +886,6 @@ var LessonsChurchProvider = class {
|
|
|
1184
886
|
const filtered = allAddOns.filter((a) => a.category === decodedCategory);
|
|
1185
887
|
return filtered.map((addOn) => createFolder(addOn.id, addOn.name, `${currentPath}/${addOn.id}`, addOn.image, true));
|
|
1186
888
|
}
|
|
1187
|
-
// async getPresentations(path: string, _auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
1188
|
-
// const venueId = getSegment(path, 4);
|
|
1189
|
-
// if (venueId) {
|
|
1190
|
-
// const venueData = await apiRequest<FeedVenueInterface>(`/venues/public/feed/${venueId}`);
|
|
1191
|
-
// if (!venueData) return null;
|
|
1192
|
-
// return convertVenueToPlan(venueData);
|
|
1193
|
-
// }
|
|
1194
|
-
// const { segments } = parsePath(path);
|
|
1195
|
-
// if (segments[0] === "addons" && segments.length === 2) {
|
|
1196
|
-
// return convertAddOnCategoryToPlan(segments[1]);
|
|
1197
|
-
// }
|
|
1198
|
-
// return null;
|
|
1199
|
-
// }
|
|
1200
889
|
async getInstructions(path, _auth) {
|
|
1201
890
|
const venueId = getSegment(path, 4);
|
|
1202
891
|
if (venueId) {
|
|
@@ -1318,42 +1007,6 @@ async function refreshTokenWithSecret(config, auth, clientSecret) {
|
|
|
1318
1007
|
return null;
|
|
1319
1008
|
}
|
|
1320
1009
|
}
|
|
1321
|
-
async function initiateDeviceFlow(config) {
|
|
1322
|
-
if (!config.supportsDeviceFlow || !config.deviceAuthEndpoint) return null;
|
|
1323
|
-
try {
|
|
1324
|
-
const response = await fetch(`${config.oauthBase}${config.deviceAuthEndpoint}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ client_id: config.clientId, scope: config.scopes.join(" ") }) });
|
|
1325
|
-
if (!response.ok) {
|
|
1326
|
-
return null;
|
|
1327
|
-
}
|
|
1328
|
-
return await response.json();
|
|
1329
|
-
} catch {
|
|
1330
|
-
return null;
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
async function pollDeviceFlowToken(config, deviceCode) {
|
|
1334
|
-
try {
|
|
1335
|
-
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 }) });
|
|
1336
|
-
if (response.ok) {
|
|
1337
|
-
const data = await response.json();
|
|
1338
|
-
return { access_token: data.access_token, refresh_token: data.refresh_token, token_type: data.token_type || "Bearer", created_at: Math.floor(Date.now() / 1e3), expires_in: data.expires_in, scope: data.scope || config.scopes.join(" ") };
|
|
1339
|
-
}
|
|
1340
|
-
const errorData = await response.json();
|
|
1341
|
-
switch (errorData.error) {
|
|
1342
|
-
case "authorization_pending":
|
|
1343
|
-
return { error: "authorization_pending" };
|
|
1344
|
-
case "slow_down":
|
|
1345
|
-
return { error: "slow_down", shouldSlowDown: true };
|
|
1346
|
-
case "expired_token":
|
|
1347
|
-
return null;
|
|
1348
|
-
case "access_denied":
|
|
1349
|
-
return null;
|
|
1350
|
-
default:
|
|
1351
|
-
return null;
|
|
1352
|
-
}
|
|
1353
|
-
} catch {
|
|
1354
|
-
return { error: "network_error" };
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
1010
|
|
|
1358
1011
|
// src/providers/b1Church/B1ChurchApi.ts
|
|
1359
1012
|
var API_BASE3 = "https://api.churchapps.org";
|
|
@@ -1692,6 +1345,7 @@ function isExternalProviderItem(item) {
|
|
|
1692
1345
|
var B1ChurchProvider = class {
|
|
1693
1346
|
constructor() {
|
|
1694
1347
|
this.apiHelper = new ApiHelper();
|
|
1348
|
+
this.deviceFlowHelper = new DeviceFlowHelper();
|
|
1695
1349
|
// Unified cache for external provider data to avoid duplicate calls across methods
|
|
1696
1350
|
this.externalContentCache = {
|
|
1697
1351
|
plans: /* @__PURE__ */ new Map(),
|
|
@@ -1705,109 +1359,7 @@ var B1ChurchProvider = class {
|
|
|
1705
1359
|
this.appBase = "https://admin.b1.church";
|
|
1706
1360
|
this.requiresAuth = true;
|
|
1707
1361
|
this.authTypes = ["oauth_pkce", "device_flow"];
|
|
1708
|
-
this.capabilities = { browse: true,
|
|
1709
|
-
// async getPresentations(path: string, authData?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
1710
|
-
// const { segments, depth } = parsePath(path);
|
|
1711
|
-
// if (depth < 4 || segments[0] !== "ministries") return null;
|
|
1712
|
-
// const ministryId = segments[1];
|
|
1713
|
-
// const planId = segments[3];
|
|
1714
|
-
// const planTypeId = segments[2];
|
|
1715
|
-
// // Need to fetch plan details to get churchId and contentId
|
|
1716
|
-
// const plans = await fetchPlans(planTypeId, authData);
|
|
1717
|
-
// const planFolder = plans.find(p => p.id === planId);
|
|
1718
|
-
// if (!planFolder) return null;
|
|
1719
|
-
// const churchId = planFolder.churchId;
|
|
1720
|
-
// const venueId = planFolder.contentId;
|
|
1721
|
-
// const planTitle = planFolder.name || "Plan";
|
|
1722
|
-
// if (!churchId) {
|
|
1723
|
-
// console.warn("[B1Church getPresentations] planFolder missing churchId:", planFolder.id);
|
|
1724
|
-
// return null;
|
|
1725
|
-
// }
|
|
1726
|
-
// const pathFn = this.config.endpoints.planItems as (churchId: string, planId: string) => string;
|
|
1727
|
-
// const planItems = await this.apiRequest<B1PlanItem[]>(pathFn(churchId, planId), authData);
|
|
1728
|
-
// // If no planItems but plan has associated provider content, fetch from that provider
|
|
1729
|
-
// if ((!planItems || planItems.length === 0) && planFolder.providerId && planFolder.providerPlanId) {
|
|
1730
|
-
// const externalPlan = await fetchFromProviderProxy(
|
|
1731
|
-
// "getPresentations",
|
|
1732
|
-
// ministryId,
|
|
1733
|
-
// planFolder.providerId,
|
|
1734
|
-
// planFolder.providerPlanId,
|
|
1735
|
-
// authData
|
|
1736
|
-
// );
|
|
1737
|
-
// if (externalPlan) {
|
|
1738
|
-
// return { id: planId, name: planTitle, sections: externalPlan.sections, allFiles: externalPlan.allFiles };
|
|
1739
|
-
// }
|
|
1740
|
-
// }
|
|
1741
|
-
// if (!planItems || !Array.isArray(planItems)) return null;
|
|
1742
|
-
// const venueFeed = venueId ? await fetchVenueFeed(venueId) : null;
|
|
1743
|
-
// const sections: PlanSection[] = [];
|
|
1744
|
-
// const allFiles: ContentFile[] = [];
|
|
1745
|
-
// for (const sectionItem of planItems) {
|
|
1746
|
-
// const presentations: PlanPresentation[] = [];
|
|
1747
|
-
// for (const child of sectionItem.children || []) {
|
|
1748
|
-
// // Try external provider resolution first (cached, uses providerContentPath)
|
|
1749
|
-
// if (isExternalProviderItem(child) && child.providerId && child.providerPath) {
|
|
1750
|
-
// const cacheKey = `${child.providerId}:${child.providerPath}`;
|
|
1751
|
-
// let externalPlan = this.externalContentCache.plans.get(cacheKey);
|
|
1752
|
-
// if (externalPlan === undefined) {
|
|
1753
|
-
// externalPlan = await fetchFromProviderProxy(
|
|
1754
|
-
// "getPresentations",
|
|
1755
|
-
// ministryId,
|
|
1756
|
-
// child.providerId,
|
|
1757
|
-
// child.providerPath,
|
|
1758
|
-
// authData
|
|
1759
|
-
// );
|
|
1760
|
-
// this.externalContentCache.plans.set(cacheKey, externalPlan);
|
|
1761
|
-
// }
|
|
1762
|
-
// if (externalPlan) {
|
|
1763
|
-
// if (child.providerContentPath) {
|
|
1764
|
-
// // Fetch instructions to enable path-based lookup (with caching)
|
|
1765
|
-
// let externalInstructions = this.externalContentCache.instructions.get(cacheKey);
|
|
1766
|
-
// if (externalInstructions === undefined) {
|
|
1767
|
-
// externalInstructions = await fetchFromProviderProxy(
|
|
1768
|
-
// "getInstructions",
|
|
1769
|
-
// ministryId,
|
|
1770
|
-
// child.providerId,
|
|
1771
|
-
// child.providerPath,
|
|
1772
|
-
// authData
|
|
1773
|
-
// );
|
|
1774
|
-
// this.externalContentCache.instructions.set(cacheKey, externalInstructions);
|
|
1775
|
-
// }
|
|
1776
|
-
// // Find and use only the specific presentation
|
|
1777
|
-
// const matchingPresentation = this.findPresentationByPath(externalPlan, externalInstructions, child.providerContentPath);
|
|
1778
|
-
// if (matchingPresentation) {
|
|
1779
|
-
// presentations.push(matchingPresentation);
|
|
1780
|
-
// if (Array.isArray(matchingPresentation.files)) {
|
|
1781
|
-
// allFiles.push(...matchingPresentation.files);
|
|
1782
|
-
// }
|
|
1783
|
-
// }
|
|
1784
|
-
// } else {
|
|
1785
|
-
// // Add all presentations from the external plan
|
|
1786
|
-
// for (const section of externalPlan.sections || []) {
|
|
1787
|
-
// if (Array.isArray(section.presentations)) {
|
|
1788
|
-
// presentations.push(...section.presentations);
|
|
1789
|
-
// }
|
|
1790
|
-
// }
|
|
1791
|
-
// if (Array.isArray(externalPlan.allFiles)) {
|
|
1792
|
-
// allFiles.push(...externalPlan.allFiles);
|
|
1793
|
-
// }
|
|
1794
|
-
// }
|
|
1795
|
-
// }
|
|
1796
|
-
// } else {
|
|
1797
|
-
// // Handle internal items (venue feed sections, link-based files, etc.)
|
|
1798
|
-
// const presentation = await planItemToPresentation(child, venueFeed);
|
|
1799
|
-
// if (presentation) {
|
|
1800
|
-
// presentations.push(presentation);
|
|
1801
|
-
// allFiles.push(...presentation.files);
|
|
1802
|
-
// }
|
|
1803
|
-
// }
|
|
1804
|
-
// }
|
|
1805
|
-
// if (presentations.length > 0 || sectionItem.label) {
|
|
1806
|
-
// sections.push({ id: sectionItem.id, name: sectionItem.label || "Section", presentations });
|
|
1807
|
-
// }
|
|
1808
|
-
// }
|
|
1809
|
-
// return { id: planId, name: planTitle, sections, allFiles };
|
|
1810
|
-
// }
|
|
1362
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
1811
1363
|
this.planTypeId = null;
|
|
1812
1364
|
}
|
|
1813
1365
|
async apiRequest(path, authData) {
|
|
@@ -1826,10 +1378,10 @@ var B1ChurchProvider = class {
|
|
|
1826
1378
|
return refreshTokenWithSecret(this.config, authData, clientSecret);
|
|
1827
1379
|
}
|
|
1828
1380
|
async initiateDeviceFlow() {
|
|
1829
|
-
return initiateDeviceFlow(this.config);
|
|
1381
|
+
return this.deviceFlowHelper.initiateDeviceFlow(this.config);
|
|
1830
1382
|
}
|
|
1831
1383
|
async pollDeviceFlowToken(deviceCode) {
|
|
1832
|
-
return pollDeviceFlowToken(this.config, deviceCode);
|
|
1384
|
+
return this.deviceFlowHelper.pollDeviceFlowToken(this.config, deviceCode);
|
|
1833
1385
|
}
|
|
1834
1386
|
async browse(path, authData) {
|
|
1835
1387
|
const { segments, depth } = parsePath(path);
|
|
@@ -2246,7 +1798,6 @@ var DropboxProvider = class {
|
|
|
2246
1798
|
this.authTypes = ["oauth_pkce"];
|
|
2247
1799
|
this.capabilities = {
|
|
2248
1800
|
browse: true,
|
|
2249
|
-
presentations: false,
|
|
2250
1801
|
playlist: true,
|
|
2251
1802
|
instructions: true,
|
|
2252
1803
|
mediaLicensing: false
|
|
@@ -2458,7 +2009,7 @@ var PlanningCenterProvider = class {
|
|
|
2458
2009
|
this.ONE_WEEK_MS = 6048e5;
|
|
2459
2010
|
this.requiresAuth = true;
|
|
2460
2011
|
this.authTypes = ["oauth_pkce"];
|
|
2461
|
-
this.capabilities = { browse: true,
|
|
2012
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
2462
2013
|
}
|
|
2463
2014
|
async apiRequest(path, auth) {
|
|
2464
2015
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
@@ -2518,54 +2069,6 @@ var PlanningCenterProvider = class {
|
|
|
2518
2069
|
if (!response?.data) return [];
|
|
2519
2070
|
return response.data.map((item) => ({ type: "file", id: item.id, title: item.attributes.title || "", mediaType: "image", url: "" }));
|
|
2520
2071
|
}
|
|
2521
|
-
// async getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
2522
|
-
// const { segments, depth } = parsePath(path);
|
|
2523
|
-
// if (depth < 3 || segments[0] !== "serviceTypes") return null;
|
|
2524
|
-
// const serviceTypeId = segments[1];
|
|
2525
|
-
// const planId = segments[2];
|
|
2526
|
-
// const pathFn = this.config.endpoints.planItems as (stId: string, pId: string) => string;
|
|
2527
|
-
// const response = await this.apiRequest<{ data: PCOPlanItem[] }>(
|
|
2528
|
-
// `${pathFn(serviceTypeId, planId)}?per_page=100`,
|
|
2529
|
-
// auth
|
|
2530
|
-
// );
|
|
2531
|
-
// if (!response?.data) return null;
|
|
2532
|
-
// const plans = await this.getPlans(serviceTypeId, `/serviceTypes/${serviceTypeId}`, auth);
|
|
2533
|
-
// const plan = plans.find(p => p.id === planId);
|
|
2534
|
-
// const planTitle = plan?.title || "Plan";
|
|
2535
|
-
// const sections: PlanSection[] = [];
|
|
2536
|
-
// const allFiles: ContentFile[] = [];
|
|
2537
|
-
// let currentSection: PlanSection | null = null;
|
|
2538
|
-
// for (const item of response.data) {
|
|
2539
|
-
// const itemType = item.attributes.item_type;
|
|
2540
|
-
// if (itemType === "header") {
|
|
2541
|
-
// if (currentSection && currentSection.presentations.length > 0) sections.push(currentSection);
|
|
2542
|
-
// currentSection = { id: item.id, name: item.attributes.title || "Section", presentations: [] };
|
|
2543
|
-
// continue;
|
|
2544
|
-
// }
|
|
2545
|
-
// if (!currentSection) {
|
|
2546
|
-
// currentSection = { id: `default-${planId}`, name: "Service", presentations: [] };
|
|
2547
|
-
// }
|
|
2548
|
-
// const presentation = await convertToPresentation(this.config, item, auth);
|
|
2549
|
-
// if (presentation) {
|
|
2550
|
-
// currentSection.presentations.push(presentation);
|
|
2551
|
-
// allFiles.push(...presentation.files);
|
|
2552
|
-
// }
|
|
2553
|
-
// }
|
|
2554
|
-
// if (currentSection && currentSection.presentations.length > 0) {
|
|
2555
|
-
// sections.push(currentSection);
|
|
2556
|
-
// }
|
|
2557
|
-
// return { id: planId, name: planTitle as string, sections, allFiles };
|
|
2558
|
-
// }
|
|
2559
|
-
// async getPlaylist(path: string, auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null> {
|
|
2560
|
-
// const plan = await this.getPresentations(path, auth);
|
|
2561
|
-
// if (!plan) return null;
|
|
2562
|
-
// return plan.allFiles.length > 0 ? plan.allFiles : null;
|
|
2563
|
-
// }
|
|
2564
|
-
// async getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null> {
|
|
2565
|
-
// const plan = await this.getPresentations(path, auth);
|
|
2566
|
-
// if (!plan) return null;
|
|
2567
|
-
// return buildInstructionsFromPlan(plan);
|
|
2568
|
-
// }
|
|
2569
2072
|
supportsDeviceFlow() {
|
|
2570
2073
|
return false;
|
|
2571
2074
|
}
|
|
@@ -4985,7 +4488,6 @@ var BibleProjectProvider = class {
|
|
|
4985
4488
|
this.authTypes = ["none"];
|
|
4986
4489
|
this.capabilities = {
|
|
4987
4490
|
browse: true,
|
|
4988
|
-
presentations: true,
|
|
4989
4491
|
playlist: true,
|
|
4990
4492
|
instructions: true,
|
|
4991
4493
|
mediaLicensing: false
|
|
@@ -5038,32 +4540,6 @@ var BibleProjectProvider = class {
|
|
|
5038
4540
|
if (!video) return [];
|
|
5039
4541
|
return [createFile(video.id, video.title, video.videoUrl, { mediaType: "video", muxPlaybackId: video.muxPlaybackId, seconds: 0 })];
|
|
5040
4542
|
}
|
|
5041
|
-
// async getPresentations(path: string, _auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
5042
|
-
// const { segments, depth } = parsePath(path);
|
|
5043
|
-
// if (depth < 1) return null;
|
|
5044
|
-
// const collectionSlug = segments[0];
|
|
5045
|
-
// const collection = this.data.collections.find(c => slugify(c.name) === collectionSlug);
|
|
5046
|
-
// if (!collection) return null;
|
|
5047
|
-
// // For collection level (depth 1), create a plan with all videos
|
|
5048
|
-
// if (depth === 1) {
|
|
5049
|
-
// const allFiles: ContentFile[] = [];
|
|
5050
|
-
// const presentations: PlanPresentation[] = collection.videos.map(video => {
|
|
5051
|
-
// const file: ContentFile = { type: "file", id: video.id, title: video.title, mediaType: "video", url: video.videoUrl, thumbnail: this.getMuxThumbnail(video.muxPlaybackId), muxPlaybackId: video.muxPlaybackId, seconds: 0 };
|
|
5052
|
-
// allFiles.push(file);
|
|
5053
|
-
// return { id: video.id, name: video.title, actionType: "play" as const, files: [file] };
|
|
5054
|
-
// });
|
|
5055
|
-
// return { id: slugify(collection.name), name: collection.name, thumbnail: collection.image || undefined, sections: [{ id: "videos", name: "Videos", presentations }], allFiles };
|
|
5056
|
-
// }
|
|
5057
|
-
// // For video level (depth 2, single video), create a simple plan
|
|
5058
|
-
// if (depth === 2) {
|
|
5059
|
-
// const videoId = segments[1];
|
|
5060
|
-
// const video = collection.videos.find(v => v.id === videoId);
|
|
5061
|
-
// if (!video) return null;
|
|
5062
|
-
// const file: ContentFile = { type: "file", id: video.id, title: video.title, mediaType: "video", url: video.videoUrl, thumbnail: this.getMuxThumbnail(video.muxPlaybackId), muxPlaybackId: video.muxPlaybackId, seconds: 0 };
|
|
5063
|
-
// return { id: video.id, name: video.title, thumbnail: this.getMuxThumbnail(video.muxPlaybackId), sections: [{ id: "main", name: "Content", presentations: [{ id: video.id, name: video.title, actionType: "play", files: [file] }] }], allFiles: [file] };
|
|
5064
|
-
// }
|
|
5065
|
-
// return null;
|
|
5066
|
-
// }
|
|
5067
4543
|
async getPlaylist(path, _auth, _resolution) {
|
|
5068
4544
|
const { segments, depth } = parsePath(path);
|
|
5069
4545
|
if (depth < 1) return null;
|
|
@@ -12715,7 +12191,7 @@ function groupFilesIntoActions(files, thumbnail) {
|
|
|
12715
12191
|
const flushGroup = () => {
|
|
12716
12192
|
if (currentGroup.length === 0) return;
|
|
12717
12193
|
const children = currentGroup.map((file) => {
|
|
12718
|
-
const seconds =
|
|
12194
|
+
const seconds = file.mediaType === "image" ? IMAGE_DURATION_SECONDS : 0;
|
|
12719
12195
|
return {
|
|
12720
12196
|
id: file.id,
|
|
12721
12197
|
itemType: "file",
|
|
@@ -12753,7 +12229,7 @@ function groupFilesIntoActions(files, thumbnail) {
|
|
|
12753
12229
|
function buildStudyInstructions(study) {
|
|
12754
12230
|
const lessonItems = study.lessons.map((lesson) => {
|
|
12755
12231
|
const fileItems = lesson.files.map((file) => {
|
|
12756
|
-
const seconds =
|
|
12232
|
+
const seconds = file.mediaType === "image" ? IMAGE_DURATION_SECONDS : 0;
|
|
12757
12233
|
return { id: file.id, itemType: "file", label: file.title, seconds, downloadUrl: file.url, thumbnail: lesson.image };
|
|
12758
12234
|
});
|
|
12759
12235
|
return { id: lesson.id, itemType: "action", label: lesson.name, actionType: "play", children: fileItems };
|
|
@@ -12788,7 +12264,6 @@ var HighVoltageKidsProvider = class {
|
|
|
12788
12264
|
this.authTypes = ["none"];
|
|
12789
12265
|
this.capabilities = {
|
|
12790
12266
|
browse: true,
|
|
12791
|
-
presentations: true,
|
|
12792
12267
|
playlist: true,
|
|
12793
12268
|
instructions: true,
|
|
12794
12269
|
mediaLicensing: false
|
|
@@ -12802,19 +12277,6 @@ var HighVoltageKidsProvider = class {
|
|
|
12802
12277
|
if (depth === 3) return getLessonFiles(this.data, segments[0], segments[1], segments[2]);
|
|
12803
12278
|
return [];
|
|
12804
12279
|
}
|
|
12805
|
-
// async getPresentations(path: string, _auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
12806
|
-
// const { segments, depth } = parsePath(path);
|
|
12807
|
-
// if (depth < 2) return null;
|
|
12808
|
-
// const study = findStudy(this.data, segments[0], segments[1]);
|
|
12809
|
-
// if (!study) return null;
|
|
12810
|
-
// if (depth === 2) return buildStudyPlan(study);
|
|
12811
|
-
// if (depth === 3) {
|
|
12812
|
-
// const lesson = findLesson(this.data, segments[0], segments[1], segments[2]);
|
|
12813
|
-
// if (!lesson) return null;
|
|
12814
|
-
// return buildLessonPlan(lesson);
|
|
12815
|
-
// }
|
|
12816
|
-
// return null;
|
|
12817
|
-
// }
|
|
12818
12280
|
async getPlaylist(path, _auth, _resolution) {
|
|
12819
12281
|
const { segments, depth } = parsePath(path);
|
|
12820
12282
|
if (depth < 2) return null;
|
|
@@ -12880,7 +12342,6 @@ var JesusFilmProvider = class {
|
|
|
12880
12342
|
this.authTypes = ["none"];
|
|
12881
12343
|
this.capabilities = {
|
|
12882
12344
|
browse: true,
|
|
12883
|
-
presentations: true,
|
|
12884
12345
|
playlist: true,
|
|
12885
12346
|
instructions: true,
|
|
12886
12347
|
mediaLicensing: false
|
|
@@ -13263,7 +12724,7 @@ var CbnProvider = class {
|
|
|
13263
12724
|
};
|
|
13264
12725
|
this.requiresAuth = true;
|
|
13265
12726
|
this.authTypes = ["device_flow"];
|
|
13266
|
-
this.capabilities = { browse: true,
|
|
12727
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
13267
12728
|
}
|
|
13268
12729
|
async apiRequest(path, auth) {
|
|
13269
12730
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
@@ -16965,7 +16426,6 @@ var LifeChurchProvider = class {
|
|
|
16965
16426
|
this.authTypes = ["none"];
|
|
16966
16427
|
this.capabilities = {
|
|
16967
16428
|
browse: true,
|
|
16968
|
-
presentations: true,
|
|
16969
16429
|
playlist: true,
|
|
16970
16430
|
instructions: true,
|
|
16971
16431
|
mediaLicensing: false
|
|
@@ -17215,28 +16675,20 @@ var unimplementedProviders = [
|
|
|
17215
16675
|
}
|
|
17216
16676
|
];
|
|
17217
16677
|
function initializeProviders() {
|
|
17218
|
-
const
|
|
17219
|
-
|
|
17220
|
-
|
|
17221
|
-
|
|
17222
|
-
|
|
17223
|
-
|
|
17224
|
-
|
|
17225
|
-
|
|
17226
|
-
|
|
17227
|
-
|
|
17228
|
-
|
|
17229
|
-
|
|
17230
|
-
|
|
17231
|
-
|
|
17232
|
-
providerRegistry.set(bibleProject.id, bibleProject);
|
|
17233
|
-
providerRegistry.set(highVoltageKids.id, highVoltageKids);
|
|
17234
|
-
providerRegistry.set(jesusFilm.id, jesusFilm);
|
|
17235
|
-
providerRegistry.set(lessonsChurch.id, lessonsChurch);
|
|
17236
|
-
providerRegistry.set(lifeChurch.id, lifeChurch);
|
|
17237
|
-
providerRegistry.set(planningCenter.id, planningCenter);
|
|
17238
|
-
providerRegistry.set(signPresenter.id, signPresenter);
|
|
17239
|
-
providerRegistry.set(cbn.id, cbn);
|
|
16678
|
+
const providers = [
|
|
16679
|
+
new APlayProvider(),
|
|
16680
|
+
new B1ChurchProvider(),
|
|
16681
|
+
new DropboxProvider(),
|
|
16682
|
+
new BibleProjectProvider(),
|
|
16683
|
+
new HighVoltageKidsProvider(),
|
|
16684
|
+
new JesusFilmProvider(),
|
|
16685
|
+
new LessonsChurchProvider(),
|
|
16686
|
+
new LifeChurchProvider(),
|
|
16687
|
+
new PlanningCenterProvider(),
|
|
16688
|
+
new SignPresenterProvider(),
|
|
16689
|
+
new CbnProvider()
|
|
16690
|
+
];
|
|
16691
|
+
for (const provider of providers) registerProvider(provider);
|
|
17240
16692
|
}
|
|
17241
16693
|
initializeProviders();
|
|
17242
16694
|
function getProviderConfig(providerId) {
|
|
@@ -17260,7 +16712,7 @@ function getAvailableProviders(ids) {
|
|
|
17260
16712
|
implemented: false,
|
|
17261
16713
|
requiresAuth: false,
|
|
17262
16714
|
authTypes: [],
|
|
17263
|
-
capabilities: { browse: false,
|
|
16715
|
+
capabilities: { browse: false, playlist: false, instructions: false, mediaLicensing: false }
|
|
17264
16716
|
}));
|
|
17265
16717
|
const all = [...implemented, ...comingSoon];
|
|
17266
16718
|
if (ids && ids.length > 0) {
|
|
@@ -17271,46 +16723,28 @@ function getAvailableProviders(ids) {
|
|
|
17271
16723
|
}
|
|
17272
16724
|
|
|
17273
16725
|
// src/index.ts
|
|
17274
|
-
var VERSION = "0.0
|
|
16726
|
+
var VERSION = "0.4.0";
|
|
17275
16727
|
export {
|
|
17276
16728
|
APlayProvider,
|
|
17277
|
-
ApiHelper,
|
|
17278
16729
|
B1ChurchProvider,
|
|
17279
16730
|
BibleProjectProvider,
|
|
17280
16731
|
CbnProvider,
|
|
17281
|
-
DEFAULT_DURATION_CONFIG,
|
|
17282
|
-
DeviceFlowHelper,
|
|
17283
16732
|
DropboxProvider,
|
|
17284
|
-
FormatConverters_exports as FormatConverters,
|
|
17285
|
-
FormatResolver,
|
|
17286
16733
|
HighVoltageKidsProvider,
|
|
17287
16734
|
JesusFilmProvider,
|
|
17288
16735
|
LessonsChurchProvider,
|
|
17289
|
-
|
|
16736
|
+
LifeChurchProvider,
|
|
17290
16737
|
PlanningCenterProvider,
|
|
17291
16738
|
SignPresenterProvider,
|
|
17292
16739
|
TokenHelper,
|
|
17293
16740
|
VERSION,
|
|
17294
|
-
appendToPath,
|
|
17295
|
-
buildPath,
|
|
17296
|
-
countWords,
|
|
17297
|
-
createFile,
|
|
17298
|
-
createFolder,
|
|
17299
|
-
detectMediaType,
|
|
17300
|
-
estimateDuration,
|
|
17301
|
-
estimateImageDuration,
|
|
17302
|
-
estimateTextDuration,
|
|
17303
|
-
generatePath,
|
|
17304
16741
|
getAllProviders,
|
|
17305
16742
|
getAvailableProviders,
|
|
17306
16743
|
getProvider,
|
|
17307
16744
|
getProviderConfig,
|
|
17308
|
-
getSegment,
|
|
17309
16745
|
isContentFile,
|
|
17310
16746
|
isContentFolder,
|
|
17311
|
-
isMediaFile,
|
|
17312
16747
|
navigateToPath,
|
|
17313
|
-
parsePath,
|
|
17314
16748
|
registerProvider
|
|
17315
16749
|
};
|
|
17316
16750
|
//# sourceMappingURL=index.js.map
|