@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.cjs
CHANGED
|
@@ -21,43 +21,25 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
APlayProvider: () => APlayProvider,
|
|
24
|
-
ApiHelper: () => ApiHelper,
|
|
25
24
|
B1ChurchProvider: () => B1ChurchProvider,
|
|
26
25
|
BibleProjectProvider: () => BibleProjectProvider,
|
|
27
26
|
CbnProvider: () => CbnProvider,
|
|
28
|
-
DEFAULT_DURATION_CONFIG: () => DEFAULT_DURATION_CONFIG,
|
|
29
|
-
DeviceFlowHelper: () => DeviceFlowHelper,
|
|
30
27
|
DropboxProvider: () => DropboxProvider,
|
|
31
|
-
FormatConverters: () => FormatConverters_exports,
|
|
32
|
-
FormatResolver: () => FormatResolver,
|
|
33
28
|
HighVoltageKidsProvider: () => HighVoltageKidsProvider,
|
|
34
29
|
JesusFilmProvider: () => JesusFilmProvider,
|
|
35
30
|
LessonsChurchProvider: () => LessonsChurchProvider,
|
|
36
|
-
|
|
31
|
+
LifeChurchProvider: () => LifeChurchProvider,
|
|
37
32
|
PlanningCenterProvider: () => PlanningCenterProvider,
|
|
38
33
|
SignPresenterProvider: () => SignPresenterProvider,
|
|
39
34
|
TokenHelper: () => TokenHelper,
|
|
40
35
|
VERSION: () => VERSION,
|
|
41
|
-
appendToPath: () => appendToPath,
|
|
42
|
-
buildPath: () => buildPath,
|
|
43
|
-
countWords: () => countWords,
|
|
44
|
-
createFile: () => createFile,
|
|
45
|
-
createFolder: () => createFolder,
|
|
46
|
-
detectMediaType: () => detectMediaType,
|
|
47
|
-
estimateDuration: () => estimateDuration,
|
|
48
|
-
estimateImageDuration: () => estimateImageDuration,
|
|
49
|
-
estimateTextDuration: () => estimateTextDuration,
|
|
50
|
-
generatePath: () => generatePath,
|
|
51
36
|
getAllProviders: () => getAllProviders,
|
|
52
37
|
getAvailableProviders: () => getAvailableProviders,
|
|
53
38
|
getProvider: () => getProvider,
|
|
54
39
|
getProviderConfig: () => getProviderConfig,
|
|
55
|
-
getSegment: () => getSegment,
|
|
56
40
|
isContentFile: () => isContentFile,
|
|
57
41
|
isContentFolder: () => isContentFolder,
|
|
58
|
-
isMediaFile: () => isMediaFile,
|
|
59
42
|
navigateToPath: () => navigateToPath,
|
|
60
|
-
parsePath: () => parsePath,
|
|
61
43
|
registerProvider: () => registerProvider
|
|
62
44
|
});
|
|
63
45
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -70,64 +52,6 @@ function isContentFile(item) {
|
|
|
70
52
|
return item.type === "file";
|
|
71
53
|
}
|
|
72
54
|
|
|
73
|
-
// src/utils.ts
|
|
74
|
-
function slugify(text) {
|
|
75
|
-
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
76
|
-
}
|
|
77
|
-
var VIDEO_EXTENSIONS = [".mp4", ".webm", ".m3u8", ".mov", ".avi", ".mkv", ".m4v"];
|
|
78
|
-
var IMAGE_EXTENSIONS = [
|
|
79
|
-
".jpg",
|
|
80
|
-
".jpeg",
|
|
81
|
-
".png",
|
|
82
|
-
".gif",
|
|
83
|
-
".bmp",
|
|
84
|
-
".svg",
|
|
85
|
-
".webp",
|
|
86
|
-
".tiff",
|
|
87
|
-
".tif"
|
|
88
|
-
];
|
|
89
|
-
function detectMediaType(url, explicitType) {
|
|
90
|
-
if (explicitType === "video" || explicitType?.startsWith("video/")) return "video";
|
|
91
|
-
if (explicitType === "image" || explicitType?.startsWith("image/")) return "image";
|
|
92
|
-
const lower = url.toLowerCase();
|
|
93
|
-
if (VIDEO_EXTENSIONS.some((p) => lower.includes(p)) || lower.includes("stream.mux.com")) return "video";
|
|
94
|
-
return "image";
|
|
95
|
-
}
|
|
96
|
-
function isMediaFile(filename) {
|
|
97
|
-
const lower = filename.toLowerCase();
|
|
98
|
-
return [...VIDEO_EXTENSIONS, ...IMAGE_EXTENSIONS].some((ext) => lower.endsWith(ext));
|
|
99
|
-
}
|
|
100
|
-
function createFolder(id, title, path, thumbnail, isLeaf) {
|
|
101
|
-
return { type: "folder", id, title, path, thumbnail, isLeaf };
|
|
102
|
-
}
|
|
103
|
-
function createFile(id, title, url, options) {
|
|
104
|
-
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 };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// src/pathUtils.ts
|
|
108
|
-
function parsePath(path) {
|
|
109
|
-
if (!path || path === "/" || path === "") {
|
|
110
|
-
return { segments: [], depth: 0 };
|
|
111
|
-
}
|
|
112
|
-
const segments = path.replace(/^\//, "").split("/").filter(Boolean);
|
|
113
|
-
return { segments, depth: segments.length };
|
|
114
|
-
}
|
|
115
|
-
function getSegment(path, index) {
|
|
116
|
-
const { segments } = parsePath(path);
|
|
117
|
-
return segments[index] ?? null;
|
|
118
|
-
}
|
|
119
|
-
function buildPath(segments) {
|
|
120
|
-
if (segments.length === 0) return "/";
|
|
121
|
-
return "/" + segments.join("/");
|
|
122
|
-
}
|
|
123
|
-
function appendToPath(basePath, segment) {
|
|
124
|
-
if (!basePath || basePath === "/" || basePath === "") {
|
|
125
|
-
return "/" + segment;
|
|
126
|
-
}
|
|
127
|
-
const cleanBase = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
128
|
-
return cleanBase + "/" + segment;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
55
|
// src/instructionPathUtils.ts
|
|
132
56
|
function navigateToPath(instructions, path) {
|
|
133
57
|
if (!path || !instructions?.items) return null;
|
|
@@ -139,283 +63,6 @@ function navigateToPath(instructions, path) {
|
|
|
139
63
|
}
|
|
140
64
|
return current;
|
|
141
65
|
}
|
|
142
|
-
function generatePath(indices) {
|
|
143
|
-
return indices.join(".");
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// src/durationUtils.ts
|
|
147
|
-
var DEFAULT_DURATION_CONFIG = {
|
|
148
|
-
secondsPerImage: 15,
|
|
149
|
-
wordsPerMinute: 150
|
|
150
|
-
};
|
|
151
|
-
function countWords(text) {
|
|
152
|
-
if (!text || !text.trim()) return 0;
|
|
153
|
-
return text.trim().split(/\s+/).length;
|
|
154
|
-
}
|
|
155
|
-
function estimateImageDuration(config = {}) {
|
|
156
|
-
return config.secondsPerImage ?? DEFAULT_DURATION_CONFIG.secondsPerImage;
|
|
157
|
-
}
|
|
158
|
-
function estimateTextDuration(text, config = {}) {
|
|
159
|
-
const words = countWords(text);
|
|
160
|
-
const wpm = config.wordsPerMinute ?? DEFAULT_DURATION_CONFIG.wordsPerMinute;
|
|
161
|
-
return Math.ceil(words / wpm * 60);
|
|
162
|
-
}
|
|
163
|
-
function estimateDuration(mediaType, options) {
|
|
164
|
-
const config = options?.config ?? {};
|
|
165
|
-
switch (mediaType) {
|
|
166
|
-
case "image":
|
|
167
|
-
return estimateImageDuration(config);
|
|
168
|
-
case "text":
|
|
169
|
-
if (options?.wordCount) {
|
|
170
|
-
const wpm = config.wordsPerMinute ?? DEFAULT_DURATION_CONFIG.wordsPerMinute;
|
|
171
|
-
return Math.ceil(options.wordCount / wpm * 60);
|
|
172
|
-
}
|
|
173
|
-
if (options?.text) {
|
|
174
|
-
return estimateTextDuration(options.text, config);
|
|
175
|
-
}
|
|
176
|
-
return 0;
|
|
177
|
-
case "video":
|
|
178
|
-
default:
|
|
179
|
-
return 0;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// src/FormatConverters.ts
|
|
184
|
-
var FormatConverters_exports = {};
|
|
185
|
-
__export(FormatConverters_exports, {
|
|
186
|
-
expandedInstructionsToPlaylist: () => expandedInstructionsToPlaylist,
|
|
187
|
-
expandedInstructionsToPresentations: () => expandedInstructionsToPresentations,
|
|
188
|
-
instructionsToPlaylist: () => instructionsToPlaylist,
|
|
189
|
-
instructionsToPresentations: () => instructionsToPresentations,
|
|
190
|
-
playlistToExpandedInstructions: () => playlistToExpandedInstructions,
|
|
191
|
-
playlistToInstructions: () => playlistToInstructions,
|
|
192
|
-
playlistToPresentations: () => playlistToPresentations,
|
|
193
|
-
presentationsToExpandedInstructions: () => presentationsToExpandedInstructions,
|
|
194
|
-
presentationsToPlaylist: () => presentationsToPlaylist
|
|
195
|
-
});
|
|
196
|
-
function generateId() {
|
|
197
|
-
return "gen-" + Math.random().toString(36).substring(2, 11);
|
|
198
|
-
}
|
|
199
|
-
function mapItemTypeToActionType(itemType) {
|
|
200
|
-
switch (itemType) {
|
|
201
|
-
case "action":
|
|
202
|
-
case "lessonAction":
|
|
203
|
-
case "providerPresentation":
|
|
204
|
-
case "play":
|
|
205
|
-
case "addon":
|
|
206
|
-
case "add-on":
|
|
207
|
-
case "lessonAddOn":
|
|
208
|
-
case "providerFile":
|
|
209
|
-
return "play";
|
|
210
|
-
default:
|
|
211
|
-
return "other";
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
function presentationsToPlaylist(plan) {
|
|
215
|
-
if (plan.allFiles && plan.allFiles.length > 0) {
|
|
216
|
-
return [...plan.allFiles];
|
|
217
|
-
}
|
|
218
|
-
const files = [];
|
|
219
|
-
for (const section of plan.sections) {
|
|
220
|
-
for (const presentation of section.presentations) {
|
|
221
|
-
files.push(...presentation.files);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
return files;
|
|
225
|
-
}
|
|
226
|
-
function mapActionTypeToItemType(actionType) {
|
|
227
|
-
switch (actionType) {
|
|
228
|
-
case "play":
|
|
229
|
-
return "action";
|
|
230
|
-
default:
|
|
231
|
-
return "item";
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
function presentationsToExpandedInstructions(plan) {
|
|
235
|
-
return {
|
|
236
|
-
name: plan.name,
|
|
237
|
-
items: plan.sections.map((section) => ({
|
|
238
|
-
id: section.id,
|
|
239
|
-
itemType: "section",
|
|
240
|
-
label: section.name,
|
|
241
|
-
children: section.presentations.map((pres) => ({
|
|
242
|
-
id: pres.id,
|
|
243
|
-
itemType: mapActionTypeToItemType(pres.actionType),
|
|
244
|
-
label: pres.name,
|
|
245
|
-
description: pres.actionType !== "other" ? pres.actionType : void 0,
|
|
246
|
-
seconds: pres.files.reduce((sum, f) => sum + (f.seconds || 0), 0) || void 0,
|
|
247
|
-
children: pres.files.map((f) => ({
|
|
248
|
-
id: f.id,
|
|
249
|
-
itemType: "file",
|
|
250
|
-
label: f.title,
|
|
251
|
-
seconds: f.seconds,
|
|
252
|
-
downloadUrl: f.downloadUrl || f.url,
|
|
253
|
-
thumbnail: f.thumbnail
|
|
254
|
-
}))
|
|
255
|
-
}))
|
|
256
|
-
}))
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
function instructionsToPlaylist(instructions) {
|
|
260
|
-
const files = [];
|
|
261
|
-
function extractFiles(items) {
|
|
262
|
-
for (const item of items) {
|
|
263
|
-
if (item.downloadUrl && (item.itemType === "file" || !item.children?.length)) {
|
|
264
|
-
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 });
|
|
265
|
-
}
|
|
266
|
-
if (item.children) {
|
|
267
|
-
extractFiles(item.children);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
extractFiles(instructions.items);
|
|
272
|
-
return files;
|
|
273
|
-
}
|
|
274
|
-
var expandedInstructionsToPlaylist = instructionsToPlaylist;
|
|
275
|
-
function instructionsToPresentations(instructions, planId) {
|
|
276
|
-
const allFiles = [];
|
|
277
|
-
const sections = instructions.items.filter((item) => item.children && item.children.length > 0).map((sectionItem) => {
|
|
278
|
-
const presentations = (sectionItem.children || []).map((presItem) => {
|
|
279
|
-
const files = [];
|
|
280
|
-
if (presItem.children && presItem.children.length > 0) {
|
|
281
|
-
for (const child of presItem.children) {
|
|
282
|
-
if (child.downloadUrl) {
|
|
283
|
-
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 };
|
|
284
|
-
allFiles.push(file);
|
|
285
|
-
files.push(file);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
if (files.length === 0 && presItem.downloadUrl) {
|
|
290
|
-
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 };
|
|
291
|
-
allFiles.push(file);
|
|
292
|
-
files.push(file);
|
|
293
|
-
}
|
|
294
|
-
return { id: presItem.id || presItem.relatedId || generateId(), name: presItem.label || "Presentation", actionType: mapItemTypeToActionType(presItem.itemType), files };
|
|
295
|
-
});
|
|
296
|
-
return { id: sectionItem.id || sectionItem.relatedId || generateId(), name: sectionItem.label || "Section", presentations };
|
|
297
|
-
});
|
|
298
|
-
return { id: planId || generateId(), name: instructions.name || "Plan", sections, allFiles };
|
|
299
|
-
}
|
|
300
|
-
var expandedInstructionsToPresentations = instructionsToPresentations;
|
|
301
|
-
function playlistToPresentations(files, planName = "Playlist", sectionName = "Content") {
|
|
302
|
-
const presentations = files.map((file, index) => ({ id: `pres-${index}-${file.id}`, name: file.title, actionType: "play", files: [file] }));
|
|
303
|
-
return { id: "playlist-plan-" + generateId(), name: planName, sections: [{ id: "main-section", name: sectionName, presentations }], allFiles: [...files] };
|
|
304
|
-
}
|
|
305
|
-
function playlistToInstructions(files, name = "Playlist") {
|
|
306
|
-
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 })) }] };
|
|
307
|
-
}
|
|
308
|
-
var playlistToExpandedInstructions = playlistToInstructions;
|
|
309
|
-
|
|
310
|
-
// src/FormatResolver.ts
|
|
311
|
-
var FormatResolver = class {
|
|
312
|
-
constructor(provider, options = {}) {
|
|
313
|
-
this.provider = provider;
|
|
314
|
-
this.options = { allowLossy: options.allowLossy ?? true };
|
|
315
|
-
}
|
|
316
|
-
getProvider() {
|
|
317
|
-
return this.provider;
|
|
318
|
-
}
|
|
319
|
-
/** Extract the last segment from a path to use as fallback ID/title */
|
|
320
|
-
getIdFromPath(path) {
|
|
321
|
-
const { segments } = parsePath(path);
|
|
322
|
-
return segments[segments.length - 1] || "content";
|
|
323
|
-
}
|
|
324
|
-
async getPlaylist(path, auth) {
|
|
325
|
-
const caps = this.provider.capabilities;
|
|
326
|
-
if (caps.playlist && this.provider.getPlaylist) {
|
|
327
|
-
const result = await this.provider.getPlaylist(path, auth);
|
|
328
|
-
if (result && result.length > 0) return result;
|
|
329
|
-
}
|
|
330
|
-
if (caps.instructions && this.provider.getInstructions) {
|
|
331
|
-
const expanded = await this.provider.getInstructions(path, auth);
|
|
332
|
-
if (expanded) return instructionsToPlaylist(expanded);
|
|
333
|
-
}
|
|
334
|
-
return null;
|
|
335
|
-
}
|
|
336
|
-
async getPlaylistWithMeta(path, auth) {
|
|
337
|
-
const caps = this.provider.capabilities;
|
|
338
|
-
if (caps.playlist && this.provider.getPlaylist) {
|
|
339
|
-
const result = await this.provider.getPlaylist(path, auth);
|
|
340
|
-
if (result && result.length > 0) {
|
|
341
|
-
return { data: result, meta: { isNative: true, isLossy: false } };
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
if (caps.instructions && this.provider.getInstructions) {
|
|
345
|
-
const expanded = await this.provider.getInstructions(path, auth);
|
|
346
|
-
if (expanded) return { data: instructionsToPlaylist(expanded), meta: { isNative: false, sourceFormat: "instructions", isLossy: false } };
|
|
347
|
-
}
|
|
348
|
-
return { data: null, meta: { isNative: false, isLossy: false } };
|
|
349
|
-
}
|
|
350
|
-
// async getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
351
|
-
// const caps = this.provider.capabilities;
|
|
352
|
-
// const fallbackId = this.getIdFromPath(path);
|
|
353
|
-
// if (caps.presentations) {
|
|
354
|
-
// const result = await this.provider.getPresentations(path, auth);
|
|
355
|
-
// if (result) return result;
|
|
356
|
-
// }
|
|
357
|
-
// if (caps.instructions && this.provider.getInstructions) {
|
|
358
|
-
// const expanded = await this.provider.getInstructions(path, auth);
|
|
359
|
-
// if (expanded) return Converters.instructionsToPresentations(expanded, fallbackId);
|
|
360
|
-
// }
|
|
361
|
-
// if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
362
|
-
// const playlist = await this.provider.getPlaylist(path, auth);
|
|
363
|
-
// if (playlist && playlist.length > 0) {
|
|
364
|
-
// return Converters.playlistToPresentations(playlist, fallbackId);
|
|
365
|
-
// }
|
|
366
|
-
// }
|
|
367
|
-
// return null;
|
|
368
|
-
// }
|
|
369
|
-
// async getPresentationsWithMeta(path: string, auth?: ContentProviderAuthData | null): Promise<{ data: Plan | null; meta: ResolvedFormatMeta }> {
|
|
370
|
-
// const caps = this.provider.capabilities;
|
|
371
|
-
// const fallbackId = this.getIdFromPath(path);
|
|
372
|
-
// if (caps.presentations) {
|
|
373
|
-
// const result = await this.provider.getPresentations(path, auth);
|
|
374
|
-
// if (result) {
|
|
375
|
-
// return { data: result, meta: { isNative: true, isLossy: false } };
|
|
376
|
-
// }
|
|
377
|
-
// }
|
|
378
|
-
// if (caps.instructions && this.provider.getInstructions) {
|
|
379
|
-
// const expanded = await this.provider.getInstructions(path, auth);
|
|
380
|
-
// if (expanded) return { data: Converters.instructionsToPresentations(expanded, fallbackId), meta: { isNative: false, sourceFormat: "instructions", isLossy: false } };
|
|
381
|
-
// }
|
|
382
|
-
// if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
383
|
-
// const playlist = await this.provider.getPlaylist(path, auth);
|
|
384
|
-
// if (playlist && playlist.length > 0) return { data: Converters.playlistToPresentations(playlist, fallbackId), meta: { isNative: false, sourceFormat: "playlist", isLossy: true } };
|
|
385
|
-
// }
|
|
386
|
-
// return { data: null, meta: { isNative: false, isLossy: false } };
|
|
387
|
-
// }
|
|
388
|
-
async getInstructions(path, auth) {
|
|
389
|
-
const caps = this.provider.capabilities;
|
|
390
|
-
const fallbackTitle = this.getIdFromPath(path);
|
|
391
|
-
if (caps.instructions && this.provider.getInstructions) {
|
|
392
|
-
const result = await this.provider.getInstructions(path, auth);
|
|
393
|
-
if (result) return result;
|
|
394
|
-
}
|
|
395
|
-
if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
396
|
-
const playlist = await this.provider.getPlaylist(path, auth);
|
|
397
|
-
if (playlist && playlist.length > 0) {
|
|
398
|
-
return playlistToInstructions(playlist, fallbackTitle);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return null;
|
|
402
|
-
}
|
|
403
|
-
async getInstructionsWithMeta(path, auth) {
|
|
404
|
-
const caps = this.provider.capabilities;
|
|
405
|
-
const fallbackTitle = this.getIdFromPath(path);
|
|
406
|
-
if (caps.instructions && this.provider.getInstructions) {
|
|
407
|
-
const result = await this.provider.getInstructions(path, auth);
|
|
408
|
-
if (result) {
|
|
409
|
-
return { data: result, meta: { isNative: true, isLossy: false } };
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
if (this.options.allowLossy && caps.playlist && this.provider.getPlaylist) {
|
|
413
|
-
const playlist = await this.provider.getPlaylist(path, auth);
|
|
414
|
-
if (playlist && playlist.length > 0) return { data: playlistToInstructions(playlist, fallbackTitle), meta: { isNative: false, sourceFormat: "playlist", isLossy: true } };
|
|
415
|
-
}
|
|
416
|
-
return { data: null, meta: { isNative: false, isLossy: false } };
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
66
|
|
|
420
67
|
// src/helpers/OAuthHelper.ts
|
|
421
68
|
var OAuthHelper = class {
|
|
@@ -579,6 +226,19 @@ var ApiHelper = class {
|
|
|
579
226
|
}
|
|
580
227
|
};
|
|
581
228
|
|
|
229
|
+
// src/pathUtils.ts
|
|
230
|
+
function parsePath(path) {
|
|
231
|
+
if (!path || path === "/" || path === "") {
|
|
232
|
+
return { segments: [], depth: 0 };
|
|
233
|
+
}
|
|
234
|
+
const segments = path.replace(/^\//, "").split("/").filter(Boolean);
|
|
235
|
+
return { segments, depth: segments.length };
|
|
236
|
+
}
|
|
237
|
+
function getSegment(path, index) {
|
|
238
|
+
const { segments } = parsePath(path);
|
|
239
|
+
return segments[index] ?? null;
|
|
240
|
+
}
|
|
241
|
+
|
|
582
242
|
// src/providers/aPlay/APlayApi.ts
|
|
583
243
|
var API_BASE = "https://api-prod.amazingkids.app";
|
|
584
244
|
async function checkMediaLicense(mediaId, auth) {
|
|
@@ -599,6 +259,57 @@ async function checkMediaLicense(mediaId, auth) {
|
|
|
599
259
|
}
|
|
600
260
|
}
|
|
601
261
|
|
|
262
|
+
// src/utils.ts
|
|
263
|
+
var IMAGE_DURATION_SECONDS = 15;
|
|
264
|
+
function slugify(text) {
|
|
265
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
266
|
+
}
|
|
267
|
+
var VIDEO_EXTENSIONS = [".mp4", ".webm", ".m3u8", ".mov", ".avi", ".mkv", ".m4v"];
|
|
268
|
+
var IMAGE_EXTENSIONS = [
|
|
269
|
+
".jpg",
|
|
270
|
+
".jpeg",
|
|
271
|
+
".png",
|
|
272
|
+
".gif",
|
|
273
|
+
".bmp",
|
|
274
|
+
".svg",
|
|
275
|
+
".webp",
|
|
276
|
+
".tiff",
|
|
277
|
+
".tif"
|
|
278
|
+
];
|
|
279
|
+
function detectMediaType(url, explicitType) {
|
|
280
|
+
if (explicitType === "video" || explicitType?.startsWith("video/")) return "video";
|
|
281
|
+
if (explicitType === "image" || explicitType?.startsWith("image/")) return "image";
|
|
282
|
+
const lower = url.toLowerCase();
|
|
283
|
+
if (VIDEO_EXTENSIONS.some((p) => lower.includes(p)) || lower.includes("stream.mux.com")) return "video";
|
|
284
|
+
return "image";
|
|
285
|
+
}
|
|
286
|
+
function isMediaFile(filename) {
|
|
287
|
+
const lower = filename.toLowerCase();
|
|
288
|
+
return [...VIDEO_EXTENSIONS, ...IMAGE_EXTENSIONS].some((ext) => lower.endsWith(ext));
|
|
289
|
+
}
|
|
290
|
+
function createFolder(id, title, path, thumbnail, isLeaf) {
|
|
291
|
+
return { type: "folder", id, title, path, thumbnail, isLeaf };
|
|
292
|
+
}
|
|
293
|
+
function createFile(id, title, url, options) {
|
|
294
|
+
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 };
|
|
295
|
+
}
|
|
296
|
+
function generateId() {
|
|
297
|
+
return "gen-" + Math.random().toString(36).substring(2, 11);
|
|
298
|
+
}
|
|
299
|
+
function instructionsToPlaylist(instructions) {
|
|
300
|
+
const files = [];
|
|
301
|
+
function extractFiles(items) {
|
|
302
|
+
for (const item of items) {
|
|
303
|
+
if (item.downloadUrl && (item.itemType === "file" || !item.children?.length)) {
|
|
304
|
+
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 });
|
|
305
|
+
}
|
|
306
|
+
if (item.children) extractFiles(item.children);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
extractFiles(instructions.items);
|
|
310
|
+
return files;
|
|
311
|
+
}
|
|
312
|
+
|
|
602
313
|
// src/providers/aPlay/APlayConverters.ts
|
|
603
314
|
function extractLibraryId(path) {
|
|
604
315
|
const { segments, depth } = parsePath(path);
|
|
@@ -705,7 +416,7 @@ var APlayProvider = class {
|
|
|
705
416
|
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` } };
|
|
706
417
|
this.requiresAuth = true;
|
|
707
418
|
this.authTypes = ["oauth_pkce"];
|
|
708
|
-
this.capabilities = { browse: true,
|
|
419
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: true };
|
|
709
420
|
}
|
|
710
421
|
async apiRequest(path, auth) {
|
|
711
422
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
@@ -757,13 +468,6 @@ var APlayProvider = class {
|
|
|
757
468
|
const mediaItems = extractArray(response, "data", "media");
|
|
758
469
|
return convertMediaToFiles(mediaItems);
|
|
759
470
|
}
|
|
760
|
-
// async getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
761
|
-
// const libraryId = extractLibraryId(path);
|
|
762
|
-
// if (!libraryId) return null;
|
|
763
|
-
// const files = await this.getMediaFiles(libraryId, auth) as ContentFile[];
|
|
764
|
-
// if (files.length === 0) return null;
|
|
765
|
-
// return convertFilesToPresentations(files, libraryId).plan;
|
|
766
|
-
// }
|
|
767
471
|
async getPlaylist(path, auth, _resolution) {
|
|
768
472
|
const libraryId = extractLibraryId(path);
|
|
769
473
|
if (!libraryId) return null;
|
|
@@ -797,7 +501,7 @@ var SignPresenterProvider = class {
|
|
|
797
501
|
this.config = { id: "signpresenter", name: "SignPresenter", apiBase: "https://api.signpresenter.com", oauthBase: "https://api.signpresenter.com/oauth", clientId: "lessonsscreen-tv", scopes: ["openid", "profile", "content"], supportsDeviceFlow: true, deviceAuthEndpoint: "/device/authorize", endpoints: { playlists: "/content/playlists", messages: (playlistId) => `/content/playlists/${playlistId}/messages` } };
|
|
798
502
|
this.requiresAuth = true;
|
|
799
503
|
this.authTypes = ["oauth_pkce", "device_flow"];
|
|
800
|
-
this.capabilities = { browse: true,
|
|
504
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
801
505
|
}
|
|
802
506
|
async apiRequest(path, auth) {
|
|
803
507
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
@@ -866,20 +570,6 @@ var SignPresenterProvider = class {
|
|
|
866
570
|
}
|
|
867
571
|
return files;
|
|
868
572
|
}
|
|
869
|
-
// async getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
870
|
-
// const { segments, depth } = parsePath(path);
|
|
871
|
-
// if (depth < 2 || segments[0] !== "playlists") return null;
|
|
872
|
-
// const playlistId = segments[1];
|
|
873
|
-
// const files = await this.getMessages(playlistId, auth) as ContentFile[];
|
|
874
|
-
// if (files.length === 0) return null;
|
|
875
|
-
// // Get playlist info for title
|
|
876
|
-
// const playlists = await this.getPlaylists(auth);
|
|
877
|
-
// const playlist = playlists.find(p => p.id === playlistId);
|
|
878
|
-
// const title = playlist?.title || "Playlist";
|
|
879
|
-
// const thumbnail = (playlist as Record<string, unknown> | undefined)?.image as string | undefined;
|
|
880
|
-
// const presentations: PlanPresentation[] = files.map(f => ({ id: f.id, name: f.title, actionType: "play" as const, files: [f] }));
|
|
881
|
-
// return { id: playlistId, name: title as string, thumbnail, sections: [{ id: `section-${playlistId}`, name: title as string, presentations }], allFiles: files };
|
|
882
|
-
// }
|
|
883
573
|
async getPlaylist(path, auth, _resolution) {
|
|
884
574
|
const { segments, depth } = parsePath(path);
|
|
885
575
|
if (depth < 2 || segments[0] !== "playlists") return null;
|
|
@@ -1025,7 +715,7 @@ function buildSectionActionsMap(actionsResponse, lessonImage, feedResponse) {
|
|
|
1025
715
|
for (const section of actionsResponse.sections) {
|
|
1026
716
|
if (section.id && section.actions) {
|
|
1027
717
|
sectionActionsMap.set(section.id, section.actions.map((action) => {
|
|
1028
|
-
const seconds = action.seconds ??
|
|
718
|
+
const seconds = action.seconds ?? IMAGE_DURATION_SECONDS;
|
|
1029
719
|
const rawActionType = action.actionType?.toLowerCase() || "";
|
|
1030
720
|
const hasFiles = rawActionType === "play" || rawActionType === "add-on";
|
|
1031
721
|
const thumbnail = action.id && actionThumbnailMap.get(action.id) || lessonImage;
|
|
@@ -1131,7 +821,7 @@ var LessonsChurchProvider = class {
|
|
|
1131
821
|
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}` } };
|
|
1132
822
|
this.requiresAuth = false;
|
|
1133
823
|
this.authTypes = ["none"];
|
|
1134
|
-
this.capabilities = { browse: true,
|
|
824
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
1135
825
|
}
|
|
1136
826
|
async getPlaylist(path, _auth, resolution) {
|
|
1137
827
|
const { segments } = parsePath(path);
|
|
@@ -1242,19 +932,6 @@ var LessonsChurchProvider = class {
|
|
|
1242
932
|
const filtered = allAddOns.filter((a) => a.category === decodedCategory);
|
|
1243
933
|
return filtered.map((addOn) => createFolder(addOn.id, addOn.name, `${currentPath}/${addOn.id}`, addOn.image, true));
|
|
1244
934
|
}
|
|
1245
|
-
// async getPresentations(path: string, _auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
1246
|
-
// const venueId = getSegment(path, 4);
|
|
1247
|
-
// if (venueId) {
|
|
1248
|
-
// const venueData = await apiRequest<FeedVenueInterface>(`/venues/public/feed/${venueId}`);
|
|
1249
|
-
// if (!venueData) return null;
|
|
1250
|
-
// return convertVenueToPlan(venueData);
|
|
1251
|
-
// }
|
|
1252
|
-
// const { segments } = parsePath(path);
|
|
1253
|
-
// if (segments[0] === "addons" && segments.length === 2) {
|
|
1254
|
-
// return convertAddOnCategoryToPlan(segments[1]);
|
|
1255
|
-
// }
|
|
1256
|
-
// return null;
|
|
1257
|
-
// }
|
|
1258
935
|
async getInstructions(path, _auth) {
|
|
1259
936
|
const venueId = getSegment(path, 4);
|
|
1260
937
|
if (venueId) {
|
|
@@ -1376,42 +1053,6 @@ async function refreshTokenWithSecret(config, auth, clientSecret) {
|
|
|
1376
1053
|
return null;
|
|
1377
1054
|
}
|
|
1378
1055
|
}
|
|
1379
|
-
async function initiateDeviceFlow(config) {
|
|
1380
|
-
if (!config.supportsDeviceFlow || !config.deviceAuthEndpoint) return null;
|
|
1381
|
-
try {
|
|
1382
|
-
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(" ") }) });
|
|
1383
|
-
if (!response.ok) {
|
|
1384
|
-
return null;
|
|
1385
|
-
}
|
|
1386
|
-
return await response.json();
|
|
1387
|
-
} catch {
|
|
1388
|
-
return null;
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
async function pollDeviceFlowToken(config, deviceCode) {
|
|
1392
|
-
try {
|
|
1393
|
-
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 }) });
|
|
1394
|
-
if (response.ok) {
|
|
1395
|
-
const data = await response.json();
|
|
1396
|
-
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(" ") };
|
|
1397
|
-
}
|
|
1398
|
-
const errorData = await response.json();
|
|
1399
|
-
switch (errorData.error) {
|
|
1400
|
-
case "authorization_pending":
|
|
1401
|
-
return { error: "authorization_pending" };
|
|
1402
|
-
case "slow_down":
|
|
1403
|
-
return { error: "slow_down", shouldSlowDown: true };
|
|
1404
|
-
case "expired_token":
|
|
1405
|
-
return null;
|
|
1406
|
-
case "access_denied":
|
|
1407
|
-
return null;
|
|
1408
|
-
default:
|
|
1409
|
-
return null;
|
|
1410
|
-
}
|
|
1411
|
-
} catch {
|
|
1412
|
-
return { error: "network_error" };
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
1056
|
|
|
1416
1057
|
// src/providers/b1Church/B1ChurchApi.ts
|
|
1417
1058
|
var API_BASE3 = "https://api.churchapps.org";
|
|
@@ -1750,6 +1391,7 @@ function isExternalProviderItem(item) {
|
|
|
1750
1391
|
var B1ChurchProvider = class {
|
|
1751
1392
|
constructor() {
|
|
1752
1393
|
this.apiHelper = new ApiHelper();
|
|
1394
|
+
this.deviceFlowHelper = new DeviceFlowHelper();
|
|
1753
1395
|
// Unified cache for external provider data to avoid duplicate calls across methods
|
|
1754
1396
|
this.externalContentCache = {
|
|
1755
1397
|
plans: /* @__PURE__ */ new Map(),
|
|
@@ -1763,109 +1405,7 @@ var B1ChurchProvider = class {
|
|
|
1763
1405
|
this.appBase = "https://admin.b1.church";
|
|
1764
1406
|
this.requiresAuth = true;
|
|
1765
1407
|
this.authTypes = ["oauth_pkce", "device_flow"];
|
|
1766
|
-
this.capabilities = { browse: true,
|
|
1767
|
-
// async getPresentations(path: string, authData?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
1768
|
-
// const { segments, depth } = parsePath(path);
|
|
1769
|
-
// if (depth < 4 || segments[0] !== "ministries") return null;
|
|
1770
|
-
// const ministryId = segments[1];
|
|
1771
|
-
// const planId = segments[3];
|
|
1772
|
-
// const planTypeId = segments[2];
|
|
1773
|
-
// // Need to fetch plan details to get churchId and contentId
|
|
1774
|
-
// const plans = await fetchPlans(planTypeId, authData);
|
|
1775
|
-
// const planFolder = plans.find(p => p.id === planId);
|
|
1776
|
-
// if (!planFolder) return null;
|
|
1777
|
-
// const churchId = planFolder.churchId;
|
|
1778
|
-
// const venueId = planFolder.contentId;
|
|
1779
|
-
// const planTitle = planFolder.name || "Plan";
|
|
1780
|
-
// if (!churchId) {
|
|
1781
|
-
// console.warn("[B1Church getPresentations] planFolder missing churchId:", planFolder.id);
|
|
1782
|
-
// return null;
|
|
1783
|
-
// }
|
|
1784
|
-
// const pathFn = this.config.endpoints.planItems as (churchId: string, planId: string) => string;
|
|
1785
|
-
// const planItems = await this.apiRequest<B1PlanItem[]>(pathFn(churchId, planId), authData);
|
|
1786
|
-
// // If no planItems but plan has associated provider content, fetch from that provider
|
|
1787
|
-
// if ((!planItems || planItems.length === 0) && planFolder.providerId && planFolder.providerPlanId) {
|
|
1788
|
-
// const externalPlan = await fetchFromProviderProxy(
|
|
1789
|
-
// "getPresentations",
|
|
1790
|
-
// ministryId,
|
|
1791
|
-
// planFolder.providerId,
|
|
1792
|
-
// planFolder.providerPlanId,
|
|
1793
|
-
// authData
|
|
1794
|
-
// );
|
|
1795
|
-
// if (externalPlan) {
|
|
1796
|
-
// return { id: planId, name: planTitle, sections: externalPlan.sections, allFiles: externalPlan.allFiles };
|
|
1797
|
-
// }
|
|
1798
|
-
// }
|
|
1799
|
-
// if (!planItems || !Array.isArray(planItems)) return null;
|
|
1800
|
-
// const venueFeed = venueId ? await fetchVenueFeed(venueId) : null;
|
|
1801
|
-
// const sections: PlanSection[] = [];
|
|
1802
|
-
// const allFiles: ContentFile[] = [];
|
|
1803
|
-
// for (const sectionItem of planItems) {
|
|
1804
|
-
// const presentations: PlanPresentation[] = [];
|
|
1805
|
-
// for (const child of sectionItem.children || []) {
|
|
1806
|
-
// // Try external provider resolution first (cached, uses providerContentPath)
|
|
1807
|
-
// if (isExternalProviderItem(child) && child.providerId && child.providerPath) {
|
|
1808
|
-
// const cacheKey = `${child.providerId}:${child.providerPath}`;
|
|
1809
|
-
// let externalPlan = this.externalContentCache.plans.get(cacheKey);
|
|
1810
|
-
// if (externalPlan === undefined) {
|
|
1811
|
-
// externalPlan = await fetchFromProviderProxy(
|
|
1812
|
-
// "getPresentations",
|
|
1813
|
-
// ministryId,
|
|
1814
|
-
// child.providerId,
|
|
1815
|
-
// child.providerPath,
|
|
1816
|
-
// authData
|
|
1817
|
-
// );
|
|
1818
|
-
// this.externalContentCache.plans.set(cacheKey, externalPlan);
|
|
1819
|
-
// }
|
|
1820
|
-
// if (externalPlan) {
|
|
1821
|
-
// if (child.providerContentPath) {
|
|
1822
|
-
// // Fetch instructions to enable path-based lookup (with caching)
|
|
1823
|
-
// let externalInstructions = this.externalContentCache.instructions.get(cacheKey);
|
|
1824
|
-
// if (externalInstructions === undefined) {
|
|
1825
|
-
// externalInstructions = await fetchFromProviderProxy(
|
|
1826
|
-
// "getInstructions",
|
|
1827
|
-
// ministryId,
|
|
1828
|
-
// child.providerId,
|
|
1829
|
-
// child.providerPath,
|
|
1830
|
-
// authData
|
|
1831
|
-
// );
|
|
1832
|
-
// this.externalContentCache.instructions.set(cacheKey, externalInstructions);
|
|
1833
|
-
// }
|
|
1834
|
-
// // Find and use only the specific presentation
|
|
1835
|
-
// const matchingPresentation = this.findPresentationByPath(externalPlan, externalInstructions, child.providerContentPath);
|
|
1836
|
-
// if (matchingPresentation) {
|
|
1837
|
-
// presentations.push(matchingPresentation);
|
|
1838
|
-
// if (Array.isArray(matchingPresentation.files)) {
|
|
1839
|
-
// allFiles.push(...matchingPresentation.files);
|
|
1840
|
-
// }
|
|
1841
|
-
// }
|
|
1842
|
-
// } else {
|
|
1843
|
-
// // Add all presentations from the external plan
|
|
1844
|
-
// for (const section of externalPlan.sections || []) {
|
|
1845
|
-
// if (Array.isArray(section.presentations)) {
|
|
1846
|
-
// presentations.push(...section.presentations);
|
|
1847
|
-
// }
|
|
1848
|
-
// }
|
|
1849
|
-
// if (Array.isArray(externalPlan.allFiles)) {
|
|
1850
|
-
// allFiles.push(...externalPlan.allFiles);
|
|
1851
|
-
// }
|
|
1852
|
-
// }
|
|
1853
|
-
// }
|
|
1854
|
-
// } else {
|
|
1855
|
-
// // Handle internal items (venue feed sections, link-based files, etc.)
|
|
1856
|
-
// const presentation = await planItemToPresentation(child, venueFeed);
|
|
1857
|
-
// if (presentation) {
|
|
1858
|
-
// presentations.push(presentation);
|
|
1859
|
-
// allFiles.push(...presentation.files);
|
|
1860
|
-
// }
|
|
1861
|
-
// }
|
|
1862
|
-
// }
|
|
1863
|
-
// if (presentations.length > 0 || sectionItem.label) {
|
|
1864
|
-
// sections.push({ id: sectionItem.id, name: sectionItem.label || "Section", presentations });
|
|
1865
|
-
// }
|
|
1866
|
-
// }
|
|
1867
|
-
// return { id: planId, name: planTitle, sections, allFiles };
|
|
1868
|
-
// }
|
|
1408
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
1869
1409
|
this.planTypeId = null;
|
|
1870
1410
|
}
|
|
1871
1411
|
async apiRequest(path, authData) {
|
|
@@ -1884,10 +1424,10 @@ var B1ChurchProvider = class {
|
|
|
1884
1424
|
return refreshTokenWithSecret(this.config, authData, clientSecret);
|
|
1885
1425
|
}
|
|
1886
1426
|
async initiateDeviceFlow() {
|
|
1887
|
-
return initiateDeviceFlow(this.config);
|
|
1427
|
+
return this.deviceFlowHelper.initiateDeviceFlow(this.config);
|
|
1888
1428
|
}
|
|
1889
1429
|
async pollDeviceFlowToken(deviceCode) {
|
|
1890
|
-
return pollDeviceFlowToken(this.config, deviceCode);
|
|
1430
|
+
return this.deviceFlowHelper.pollDeviceFlowToken(this.config, deviceCode);
|
|
1891
1431
|
}
|
|
1892
1432
|
async browse(path, authData) {
|
|
1893
1433
|
const { segments, depth } = parsePath(path);
|
|
@@ -2304,7 +1844,6 @@ var DropboxProvider = class {
|
|
|
2304
1844
|
this.authTypes = ["oauth_pkce"];
|
|
2305
1845
|
this.capabilities = {
|
|
2306
1846
|
browse: true,
|
|
2307
|
-
presentations: false,
|
|
2308
1847
|
playlist: true,
|
|
2309
1848
|
instructions: true,
|
|
2310
1849
|
mediaLicensing: false
|
|
@@ -2516,7 +2055,7 @@ var PlanningCenterProvider = class {
|
|
|
2516
2055
|
this.ONE_WEEK_MS = 6048e5;
|
|
2517
2056
|
this.requiresAuth = true;
|
|
2518
2057
|
this.authTypes = ["oauth_pkce"];
|
|
2519
|
-
this.capabilities = { browse: true,
|
|
2058
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
2520
2059
|
}
|
|
2521
2060
|
async apiRequest(path, auth) {
|
|
2522
2061
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
@@ -2576,54 +2115,6 @@ var PlanningCenterProvider = class {
|
|
|
2576
2115
|
if (!response?.data) return [];
|
|
2577
2116
|
return response.data.map((item) => ({ type: "file", id: item.id, title: item.attributes.title || "", mediaType: "image", url: "" }));
|
|
2578
2117
|
}
|
|
2579
|
-
// async getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
2580
|
-
// const { segments, depth } = parsePath(path);
|
|
2581
|
-
// if (depth < 3 || segments[0] !== "serviceTypes") return null;
|
|
2582
|
-
// const serviceTypeId = segments[1];
|
|
2583
|
-
// const planId = segments[2];
|
|
2584
|
-
// const pathFn = this.config.endpoints.planItems as (stId: string, pId: string) => string;
|
|
2585
|
-
// const response = await this.apiRequest<{ data: PCOPlanItem[] }>(
|
|
2586
|
-
// `${pathFn(serviceTypeId, planId)}?per_page=100`,
|
|
2587
|
-
// auth
|
|
2588
|
-
// );
|
|
2589
|
-
// if (!response?.data) return null;
|
|
2590
|
-
// const plans = await this.getPlans(serviceTypeId, `/serviceTypes/${serviceTypeId}`, auth);
|
|
2591
|
-
// const plan = plans.find(p => p.id === planId);
|
|
2592
|
-
// const planTitle = plan?.title || "Plan";
|
|
2593
|
-
// const sections: PlanSection[] = [];
|
|
2594
|
-
// const allFiles: ContentFile[] = [];
|
|
2595
|
-
// let currentSection: PlanSection | null = null;
|
|
2596
|
-
// for (const item of response.data) {
|
|
2597
|
-
// const itemType = item.attributes.item_type;
|
|
2598
|
-
// if (itemType === "header") {
|
|
2599
|
-
// if (currentSection && currentSection.presentations.length > 0) sections.push(currentSection);
|
|
2600
|
-
// currentSection = { id: item.id, name: item.attributes.title || "Section", presentations: [] };
|
|
2601
|
-
// continue;
|
|
2602
|
-
// }
|
|
2603
|
-
// if (!currentSection) {
|
|
2604
|
-
// currentSection = { id: `default-${planId}`, name: "Service", presentations: [] };
|
|
2605
|
-
// }
|
|
2606
|
-
// const presentation = await convertToPresentation(this.config, item, auth);
|
|
2607
|
-
// if (presentation) {
|
|
2608
|
-
// currentSection.presentations.push(presentation);
|
|
2609
|
-
// allFiles.push(...presentation.files);
|
|
2610
|
-
// }
|
|
2611
|
-
// }
|
|
2612
|
-
// if (currentSection && currentSection.presentations.length > 0) {
|
|
2613
|
-
// sections.push(currentSection);
|
|
2614
|
-
// }
|
|
2615
|
-
// return { id: planId, name: planTitle as string, sections, allFiles };
|
|
2616
|
-
// }
|
|
2617
|
-
// async getPlaylist(path: string, auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null> {
|
|
2618
|
-
// const plan = await this.getPresentations(path, auth);
|
|
2619
|
-
// if (!plan) return null;
|
|
2620
|
-
// return plan.allFiles.length > 0 ? plan.allFiles : null;
|
|
2621
|
-
// }
|
|
2622
|
-
// async getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null> {
|
|
2623
|
-
// const plan = await this.getPresentations(path, auth);
|
|
2624
|
-
// if (!plan) return null;
|
|
2625
|
-
// return buildInstructionsFromPlan(plan);
|
|
2626
|
-
// }
|
|
2627
2118
|
supportsDeviceFlow() {
|
|
2628
2119
|
return false;
|
|
2629
2120
|
}
|
|
@@ -5043,7 +4534,6 @@ var BibleProjectProvider = class {
|
|
|
5043
4534
|
this.authTypes = ["none"];
|
|
5044
4535
|
this.capabilities = {
|
|
5045
4536
|
browse: true,
|
|
5046
|
-
presentations: true,
|
|
5047
4537
|
playlist: true,
|
|
5048
4538
|
instructions: true,
|
|
5049
4539
|
mediaLicensing: false
|
|
@@ -5096,32 +4586,6 @@ var BibleProjectProvider = class {
|
|
|
5096
4586
|
if (!video) return [];
|
|
5097
4587
|
return [createFile(video.id, video.title, video.videoUrl, { mediaType: "video", muxPlaybackId: video.muxPlaybackId, seconds: 0 })];
|
|
5098
4588
|
}
|
|
5099
|
-
// async getPresentations(path: string, _auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
5100
|
-
// const { segments, depth } = parsePath(path);
|
|
5101
|
-
// if (depth < 1) return null;
|
|
5102
|
-
// const collectionSlug = segments[0];
|
|
5103
|
-
// const collection = this.data.collections.find(c => slugify(c.name) === collectionSlug);
|
|
5104
|
-
// if (!collection) return null;
|
|
5105
|
-
// // For collection level (depth 1), create a plan with all videos
|
|
5106
|
-
// if (depth === 1) {
|
|
5107
|
-
// const allFiles: ContentFile[] = [];
|
|
5108
|
-
// const presentations: PlanPresentation[] = collection.videos.map(video => {
|
|
5109
|
-
// 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 };
|
|
5110
|
-
// allFiles.push(file);
|
|
5111
|
-
// return { id: video.id, name: video.title, actionType: "play" as const, files: [file] };
|
|
5112
|
-
// });
|
|
5113
|
-
// return { id: slugify(collection.name), name: collection.name, thumbnail: collection.image || undefined, sections: [{ id: "videos", name: "Videos", presentations }], allFiles };
|
|
5114
|
-
// }
|
|
5115
|
-
// // For video level (depth 2, single video), create a simple plan
|
|
5116
|
-
// if (depth === 2) {
|
|
5117
|
-
// const videoId = segments[1];
|
|
5118
|
-
// const video = collection.videos.find(v => v.id === videoId);
|
|
5119
|
-
// if (!video) return null;
|
|
5120
|
-
// 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 };
|
|
5121
|
-
// 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] };
|
|
5122
|
-
// }
|
|
5123
|
-
// return null;
|
|
5124
|
-
// }
|
|
5125
4589
|
async getPlaylist(path, _auth, _resolution) {
|
|
5126
4590
|
const { segments, depth } = parsePath(path);
|
|
5127
4591
|
if (depth < 1) return null;
|
|
@@ -12773,7 +12237,7 @@ function groupFilesIntoActions(files, thumbnail) {
|
|
|
12773
12237
|
const flushGroup = () => {
|
|
12774
12238
|
if (currentGroup.length === 0) return;
|
|
12775
12239
|
const children = currentGroup.map((file) => {
|
|
12776
|
-
const seconds =
|
|
12240
|
+
const seconds = file.mediaType === "image" ? IMAGE_DURATION_SECONDS : 0;
|
|
12777
12241
|
return {
|
|
12778
12242
|
id: file.id,
|
|
12779
12243
|
itemType: "file",
|
|
@@ -12811,7 +12275,7 @@ function groupFilesIntoActions(files, thumbnail) {
|
|
|
12811
12275
|
function buildStudyInstructions(study) {
|
|
12812
12276
|
const lessonItems = study.lessons.map((lesson) => {
|
|
12813
12277
|
const fileItems = lesson.files.map((file) => {
|
|
12814
|
-
const seconds =
|
|
12278
|
+
const seconds = file.mediaType === "image" ? IMAGE_DURATION_SECONDS : 0;
|
|
12815
12279
|
return { id: file.id, itemType: "file", label: file.title, seconds, downloadUrl: file.url, thumbnail: lesson.image };
|
|
12816
12280
|
});
|
|
12817
12281
|
return { id: lesson.id, itemType: "action", label: lesson.name, actionType: "play", children: fileItems };
|
|
@@ -12846,7 +12310,6 @@ var HighVoltageKidsProvider = class {
|
|
|
12846
12310
|
this.authTypes = ["none"];
|
|
12847
12311
|
this.capabilities = {
|
|
12848
12312
|
browse: true,
|
|
12849
|
-
presentations: true,
|
|
12850
12313
|
playlist: true,
|
|
12851
12314
|
instructions: true,
|
|
12852
12315
|
mediaLicensing: false
|
|
@@ -12860,19 +12323,6 @@ var HighVoltageKidsProvider = class {
|
|
|
12860
12323
|
if (depth === 3) return getLessonFiles(this.data, segments[0], segments[1], segments[2]);
|
|
12861
12324
|
return [];
|
|
12862
12325
|
}
|
|
12863
|
-
// async getPresentations(path: string, _auth?: ContentProviderAuthData | null): Promise<Plan | null> {
|
|
12864
|
-
// const { segments, depth } = parsePath(path);
|
|
12865
|
-
// if (depth < 2) return null;
|
|
12866
|
-
// const study = findStudy(this.data, segments[0], segments[1]);
|
|
12867
|
-
// if (!study) return null;
|
|
12868
|
-
// if (depth === 2) return buildStudyPlan(study);
|
|
12869
|
-
// if (depth === 3) {
|
|
12870
|
-
// const lesson = findLesson(this.data, segments[0], segments[1], segments[2]);
|
|
12871
|
-
// if (!lesson) return null;
|
|
12872
|
-
// return buildLessonPlan(lesson);
|
|
12873
|
-
// }
|
|
12874
|
-
// return null;
|
|
12875
|
-
// }
|
|
12876
12326
|
async getPlaylist(path, _auth, _resolution) {
|
|
12877
12327
|
const { segments, depth } = parsePath(path);
|
|
12878
12328
|
if (depth < 2) return null;
|
|
@@ -12938,7 +12388,6 @@ var JesusFilmProvider = class {
|
|
|
12938
12388
|
this.authTypes = ["none"];
|
|
12939
12389
|
this.capabilities = {
|
|
12940
12390
|
browse: true,
|
|
12941
|
-
presentations: true,
|
|
12942
12391
|
playlist: true,
|
|
12943
12392
|
instructions: true,
|
|
12944
12393
|
mediaLicensing: false
|
|
@@ -13321,7 +12770,7 @@ var CbnProvider = class {
|
|
|
13321
12770
|
};
|
|
13322
12771
|
this.requiresAuth = true;
|
|
13323
12772
|
this.authTypes = ["device_flow"];
|
|
13324
|
-
this.capabilities = { browse: true,
|
|
12773
|
+
this.capabilities = { browse: true, playlist: true, instructions: true, mediaLicensing: false };
|
|
13325
12774
|
}
|
|
13326
12775
|
async apiRequest(path, auth) {
|
|
13327
12776
|
return this.apiHelper.apiRequest(this.config, this.id, path, auth);
|
|
@@ -17023,7 +16472,6 @@ var LifeChurchProvider = class {
|
|
|
17023
16472
|
this.authTypes = ["none"];
|
|
17024
16473
|
this.capabilities = {
|
|
17025
16474
|
browse: true,
|
|
17026
|
-
presentations: true,
|
|
17027
16475
|
playlist: true,
|
|
17028
16476
|
instructions: true,
|
|
17029
16477
|
mediaLicensing: false
|
|
@@ -17273,28 +16721,20 @@ var unimplementedProviders = [
|
|
|
17273
16721
|
}
|
|
17274
16722
|
];
|
|
17275
16723
|
function initializeProviders() {
|
|
17276
|
-
const
|
|
17277
|
-
|
|
17278
|
-
|
|
17279
|
-
|
|
17280
|
-
|
|
17281
|
-
|
|
17282
|
-
|
|
17283
|
-
|
|
17284
|
-
|
|
17285
|
-
|
|
17286
|
-
|
|
17287
|
-
|
|
17288
|
-
|
|
17289
|
-
|
|
17290
|
-
providerRegistry.set(bibleProject.id, bibleProject);
|
|
17291
|
-
providerRegistry.set(highVoltageKids.id, highVoltageKids);
|
|
17292
|
-
providerRegistry.set(jesusFilm.id, jesusFilm);
|
|
17293
|
-
providerRegistry.set(lessonsChurch.id, lessonsChurch);
|
|
17294
|
-
providerRegistry.set(lifeChurch.id, lifeChurch);
|
|
17295
|
-
providerRegistry.set(planningCenter.id, planningCenter);
|
|
17296
|
-
providerRegistry.set(signPresenter.id, signPresenter);
|
|
17297
|
-
providerRegistry.set(cbn.id, cbn);
|
|
16724
|
+
const providers = [
|
|
16725
|
+
new APlayProvider(),
|
|
16726
|
+
new B1ChurchProvider(),
|
|
16727
|
+
new DropboxProvider(),
|
|
16728
|
+
new BibleProjectProvider(),
|
|
16729
|
+
new HighVoltageKidsProvider(),
|
|
16730
|
+
new JesusFilmProvider(),
|
|
16731
|
+
new LessonsChurchProvider(),
|
|
16732
|
+
new LifeChurchProvider(),
|
|
16733
|
+
new PlanningCenterProvider(),
|
|
16734
|
+
new SignPresenterProvider(),
|
|
16735
|
+
new CbnProvider()
|
|
16736
|
+
];
|
|
16737
|
+
for (const provider of providers) registerProvider(provider);
|
|
17298
16738
|
}
|
|
17299
16739
|
initializeProviders();
|
|
17300
16740
|
function getProviderConfig(providerId) {
|
|
@@ -17318,7 +16758,7 @@ function getAvailableProviders(ids) {
|
|
|
17318
16758
|
implemented: false,
|
|
17319
16759
|
requiresAuth: false,
|
|
17320
16760
|
authTypes: [],
|
|
17321
|
-
capabilities: { browse: false,
|
|
16761
|
+
capabilities: { browse: false, playlist: false, instructions: false, mediaLicensing: false }
|
|
17322
16762
|
}));
|
|
17323
16763
|
const all = [...implemented, ...comingSoon];
|
|
17324
16764
|
if (ids && ids.length > 0) {
|
|
@@ -17329,47 +16769,29 @@ function getAvailableProviders(ids) {
|
|
|
17329
16769
|
}
|
|
17330
16770
|
|
|
17331
16771
|
// src/index.ts
|
|
17332
|
-
var VERSION = "0.0
|
|
16772
|
+
var VERSION = "0.4.0";
|
|
17333
16773
|
// Annotate the CommonJS export names for ESM import in node:
|
|
17334
16774
|
0 && (module.exports = {
|
|
17335
16775
|
APlayProvider,
|
|
17336
|
-
ApiHelper,
|
|
17337
16776
|
B1ChurchProvider,
|
|
17338
16777
|
BibleProjectProvider,
|
|
17339
16778
|
CbnProvider,
|
|
17340
|
-
DEFAULT_DURATION_CONFIG,
|
|
17341
|
-
DeviceFlowHelper,
|
|
17342
16779
|
DropboxProvider,
|
|
17343
|
-
FormatConverters,
|
|
17344
|
-
FormatResolver,
|
|
17345
16780
|
HighVoltageKidsProvider,
|
|
17346
16781
|
JesusFilmProvider,
|
|
17347
16782
|
LessonsChurchProvider,
|
|
17348
|
-
|
|
16783
|
+
LifeChurchProvider,
|
|
17349
16784
|
PlanningCenterProvider,
|
|
17350
16785
|
SignPresenterProvider,
|
|
17351
16786
|
TokenHelper,
|
|
17352
16787
|
VERSION,
|
|
17353
|
-
appendToPath,
|
|
17354
|
-
buildPath,
|
|
17355
|
-
countWords,
|
|
17356
|
-
createFile,
|
|
17357
|
-
createFolder,
|
|
17358
|
-
detectMediaType,
|
|
17359
|
-
estimateDuration,
|
|
17360
|
-
estimateImageDuration,
|
|
17361
|
-
estimateTextDuration,
|
|
17362
|
-
generatePath,
|
|
17363
16788
|
getAllProviders,
|
|
17364
16789
|
getAvailableProviders,
|
|
17365
16790
|
getProvider,
|
|
17366
16791
|
getProviderConfig,
|
|
17367
|
-
getSegment,
|
|
17368
16792
|
isContentFile,
|
|
17369
16793
|
isContentFolder,
|
|
17370
|
-
isMediaFile,
|
|
17371
16794
|
navigateToPath,
|
|
17372
|
-
parsePath,
|
|
17373
16795
|
registerProvider
|
|
17374
16796
|
});
|
|
17375
16797
|
//# sourceMappingURL=index.cjs.map
|