@churchapps/content-provider-helper 0.0.3 → 0.0.4

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