@churchapps/content-provider-helper 0.0.2 → 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.d.ts CHANGED
@@ -26,12 +26,12 @@ interface DeviceAuthorizationResponse {
26
26
  interval?: number;
27
27
  }
28
28
  interface DeviceFlowState {
29
- status: 'loading' | 'awaiting_user' | 'polling' | 'success' | 'error' | 'expired';
29
+ status: "loading" | "awaiting_user" | "polling" | "success" | "error" | "expired";
30
30
  deviceAuth?: DeviceAuthorizationResponse;
31
31
  error?: string;
32
32
  pollCount?: number;
33
33
  }
34
- type AuthType = 'none' | 'oauth_pkce' | 'device_flow' | 'form_login';
34
+ type AuthType = "none" | "oauth_pkce" | "device_flow" | "form_login";
35
35
  interface ProviderLogos {
36
36
  light: string;
37
37
  dark: string;
@@ -46,17 +46,18 @@ interface ProviderInfo {
46
46
  capabilities: ProviderCapabilities;
47
47
  }
48
48
  interface ContentFolder {
49
- type: 'folder';
49
+ type: "folder";
50
50
  id: string;
51
51
  title: string;
52
52
  image?: string;
53
- providerData?: Record<string, unknown>;
53
+ isLeaf?: boolean;
54
+ path: string;
54
55
  }
55
56
  interface ContentFile {
56
- type: 'file';
57
+ type: "file";
57
58
  id: string;
58
59
  title: string;
59
- mediaType: 'video' | 'image';
60
+ mediaType: "video" | "image";
60
61
  image?: string;
61
62
  url: string;
62
63
  embedUrl?: string;
@@ -64,7 +65,10 @@ interface ContentFile {
64
65
  decryptionKey?: string;
65
66
  mediaId?: string;
66
67
  pingbackUrl?: string;
67
- providerData?: Record<string, unknown>;
68
+ seconds?: number;
69
+ loop?: boolean;
70
+ loopVideo?: boolean;
71
+ streamUrl?: string;
68
72
  }
69
73
  type ContentItem = ContentFolder | ContentFile;
70
74
  declare function isContentFolder(item: ContentItem): item is ContentFolder;
@@ -76,7 +80,7 @@ type DeviceFlowPollResult = ContentProviderAuthData | {
76
80
  interface PlanPresentation {
77
81
  id: string;
78
82
  name: string;
79
- actionType: 'play' | 'add-on' | 'other';
83
+ actionType: "play" | "add-on" | "other";
80
84
  files: ContentFile[];
81
85
  }
82
86
  interface PlanSection {
@@ -154,32 +158,172 @@ interface ProviderCapabilities {
154
158
  presentations: boolean;
155
159
  playlist: boolean;
156
160
  instructions: boolean;
157
- expandedInstructions: boolean;
158
161
  mediaLicensing: boolean;
159
162
  }
160
- type MediaLicenseStatus = 'valid' | 'expired' | 'not_licensed' | 'unknown';
163
+ type MediaLicenseStatus = "valid" | "expired" | "not_licensed" | "unknown";
161
164
  interface MediaLicenseResult {
162
165
  mediaId: string;
163
166
  status: MediaLicenseStatus;
164
167
  message?: string;
165
168
  expiresAt?: string | number;
166
169
  }
170
+ /**
171
+ * Core provider interface - all providers should implement this
172
+ */
173
+ interface IProvider {
174
+ readonly id: string;
175
+ readonly name: string;
176
+ readonly logos: ProviderLogos;
177
+ readonly config: ContentProviderConfig;
178
+ readonly requiresAuth: boolean;
179
+ readonly capabilities: ProviderCapabilities;
180
+ readonly authTypes: AuthType[];
181
+ browse(path?: string | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
182
+ getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
183
+ getPlaylist?(path: string, auth?: ContentProviderAuthData | null, resolution?: number): Promise<ContentFile[] | null>;
184
+ getInstructions?(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
185
+ checkMediaLicense?(mediaId: string, auth?: ContentProviderAuthData | null): Promise<MediaLicenseResult | null>;
186
+ }
187
+ /**
188
+ * @deprecated Use IProvider instead. This interface will be removed in a future version.
189
+ */
190
+ interface IContentProvider {
191
+ readonly id: string;
192
+ readonly name: string;
193
+ readonly logos: ProviderLogos;
194
+ readonly config: ContentProviderConfig;
195
+ browse(path?: string | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
196
+ getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
197
+ getPlaylist(path: string, auth?: ContentProviderAuthData | null, resolution?: number): Promise<ContentFile[] | null>;
198
+ getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
199
+ requiresAuth(): boolean;
200
+ getCapabilities(): ProviderCapabilities;
201
+ checkMediaLicense(mediaId: string, auth?: ContentProviderAuthData | null): Promise<MediaLicenseResult | null>;
202
+ }
203
+ /**
204
+ * @deprecated Use auth helpers directly (OAuthHelper, DeviceFlowHelper, TokenHelper) with provider.config.
205
+ * This interface will be removed in a future version.
206
+ */
207
+ interface IAuthProvider {
208
+ getAuthTypes(): AuthType[];
209
+ isAuthValid(auth: ContentProviderAuthData | null | undefined): boolean;
210
+ isTokenExpired(auth: ContentProviderAuthData): boolean;
211
+ refreshToken(auth: ContentProviderAuthData): Promise<ContentProviderAuthData | null>;
212
+ generateCodeVerifier(): string;
213
+ generateCodeChallenge(verifier: string): Promise<string>;
214
+ buildAuthUrl(codeVerifier: string, redirectUri: string, state?: string): Promise<{
215
+ url: string;
216
+ challengeMethod: string;
217
+ }>;
218
+ exchangeCodeForTokens(code: string, codeVerifier: string, redirectUri: string): Promise<ContentProviderAuthData | null>;
219
+ supportsDeviceFlow(): boolean;
220
+ initiateDeviceFlow(): Promise<DeviceAuthorizationResponse | null>;
221
+ pollDeviceFlowToken(deviceCode: string): Promise<DeviceFlowPollResult>;
222
+ calculatePollDelay(baseInterval?: number, slowDownCount?: number): number;
223
+ }
224
+
225
+ declare function detectMediaType(url: string, explicitType?: string): "video" | "image";
226
+ declare function createFolder(id: string, title: string, path: string, image?: string, isLeaf?: boolean): ContentFolder;
227
+ declare function createFile(id: string, title: string, url: string, options?: {
228
+ mediaType?: "video" | "image";
229
+ image?: string;
230
+ muxPlaybackId?: string;
231
+ seconds?: number;
232
+ loop?: boolean;
233
+ loopVideo?: boolean;
234
+ streamUrl?: string;
235
+ }): ContentFile;
236
+
237
+ /**
238
+ * Path parsing utilities for content providers.
239
+ * Paths follow the format: /segment1/segment2/segment3/...
240
+ * Example: /lessons/programId/studyId/lessonId/venueId
241
+ */
242
+ interface ParsedPath {
243
+ segments: string[];
244
+ depth: number;
245
+ }
246
+ /**
247
+ * Parse a path string into segments and depth.
248
+ * @param path - The path to parse (e.g., "/lessons/abc123/def456")
249
+ * @returns Object with segments array and depth count
250
+ */
251
+ declare function parsePath(path: string | null | undefined): ParsedPath;
252
+ /**
253
+ * Get a specific segment from a path by index.
254
+ * @param path - The path to parse
255
+ * @param index - Zero-based index of the segment to retrieve
256
+ * @returns The segment at the given index, or null if not found
257
+ */
258
+ declare function getSegment(path: string | null | undefined, index: number): string | null;
259
+ /**
260
+ * Build a path string from segments.
261
+ * @param segments - Array of path segments
262
+ * @returns Path string with leading slash
263
+ */
264
+ declare function buildPath(segments: string[]): string;
265
+ /**
266
+ * Append a segment to an existing path.
267
+ * @param basePath - The base path
268
+ * @param segment - The segment to append
269
+ * @returns New path with segment appended
270
+ */
271
+ declare function appendToPath(basePath: string | null | undefined, segment: string): string;
167
272
 
168
- declare function detectMediaType(url: string, explicitType?: string): 'video' | 'image';
273
+ /**
274
+ * Navigate to a specific item using a dot-notation path.
275
+ * Path format: "0.2.1" means items[0].children[2].children[1]
276
+ */
277
+ declare function navigateToPath(instructions: Instructions, path: string): InstructionItem | null;
278
+ /**
279
+ * Generate a path string for an item given its position in the tree.
280
+ * Used when selecting an item to store its path.
281
+ */
282
+ declare function generatePath(indices: number[]): string;
283
+
284
+ interface DurationEstimationConfig {
285
+ secondsPerImage: number;
286
+ wordsPerMinute: number;
287
+ }
288
+ declare const DEFAULT_DURATION_CONFIG: DurationEstimationConfig;
289
+ /**
290
+ * Count words in text (splits on whitespace)
291
+ */
292
+ declare function countWords(text: string): number;
293
+ /**
294
+ * Estimate duration for image content
295
+ * @returns Duration in seconds (default: 15)
296
+ */
297
+ declare function estimateImageDuration(config?: Partial<DurationEstimationConfig>): number;
298
+ /**
299
+ * Estimate duration for text content based on word count
300
+ * @param text - The text content
301
+ * @param config - Optional configuration overrides
302
+ * @returns Duration in seconds
303
+ */
304
+ declare function estimateTextDuration(text: string, config?: Partial<DurationEstimationConfig>): number;
305
+ /**
306
+ * Estimate duration based on media type
307
+ * @param mediaType - "video" | "image" | "text"
308
+ * @param options - Text content or word count for text estimation
309
+ * @returns Duration in seconds (0 for video/unknown)
310
+ */
311
+ declare function estimateDuration(mediaType: "video" | "image" | "text", options?: {
312
+ text?: string;
313
+ wordCount?: number;
314
+ config?: Partial<DurationEstimationConfig>;
315
+ }): number;
169
316
 
170
317
  declare function presentationsToPlaylist(plan: Plan): ContentFile[];
171
- declare function presentationsToInstructions(plan: Plan): Instructions;
172
318
  declare function presentationsToExpandedInstructions(plan: Plan): Instructions;
173
319
  declare function instructionsToPlaylist(instructions: Instructions): ContentFile[];
174
320
  declare const expandedInstructionsToPlaylist: typeof instructionsToPlaylist;
175
321
  declare function instructionsToPresentations(instructions: Instructions, planId?: string): Plan;
176
322
  declare const expandedInstructionsToPresentations: typeof instructionsToPresentations;
177
- declare function collapseInstructions(instructions: Instructions, maxDepth?: number): Instructions;
178
323
  declare function playlistToPresentations(files: ContentFile[], planName?: string, sectionName?: string): Plan;
179
324
  declare function playlistToInstructions(files: ContentFile[], venueName?: string): Instructions;
180
325
  declare const playlistToExpandedInstructions: typeof playlistToInstructions;
181
326
 
182
- declare const FormatConverters_collapseInstructions: typeof collapseInstructions;
183
327
  declare const FormatConverters_expandedInstructionsToPlaylist: typeof expandedInstructionsToPlaylist;
184
328
  declare const FormatConverters_expandedInstructionsToPresentations: typeof expandedInstructionsToPresentations;
185
329
  declare const FormatConverters_instructionsToPlaylist: typeof instructionsToPlaylist;
@@ -188,28 +332,96 @@ declare const FormatConverters_playlistToExpandedInstructions: typeof playlistTo
188
332
  declare const FormatConverters_playlistToInstructions: typeof playlistToInstructions;
189
333
  declare const FormatConverters_playlistToPresentations: typeof playlistToPresentations;
190
334
  declare const FormatConverters_presentationsToExpandedInstructions: typeof presentationsToExpandedInstructions;
191
- declare const FormatConverters_presentationsToInstructions: typeof presentationsToInstructions;
192
335
  declare const FormatConverters_presentationsToPlaylist: typeof presentationsToPlaylist;
193
336
  declare namespace FormatConverters {
194
- export { FormatConverters_collapseInstructions as collapseInstructions, FormatConverters_expandedInstructionsToPlaylist as expandedInstructionsToPlaylist, FormatConverters_expandedInstructionsToPresentations as expandedInstructionsToPresentations, FormatConverters_instructionsToPlaylist as instructionsToPlaylist, FormatConverters_instructionsToPresentations as instructionsToPresentations, FormatConverters_playlistToExpandedInstructions as playlistToExpandedInstructions, FormatConverters_playlistToInstructions as playlistToInstructions, FormatConverters_playlistToPresentations as playlistToPresentations, FormatConverters_presentationsToExpandedInstructions as presentationsToExpandedInstructions, FormatConverters_presentationsToInstructions as presentationsToInstructions, FormatConverters_presentationsToPlaylist as presentationsToPlaylist };
337
+ export { FormatConverters_expandedInstructionsToPlaylist as expandedInstructionsToPlaylist, FormatConverters_expandedInstructionsToPresentations as expandedInstructionsToPresentations, FormatConverters_instructionsToPlaylist as instructionsToPlaylist, FormatConverters_instructionsToPresentations as instructionsToPresentations, FormatConverters_playlistToExpandedInstructions as playlistToExpandedInstructions, FormatConverters_playlistToInstructions as playlistToInstructions, FormatConverters_playlistToPresentations as playlistToPresentations, FormatConverters_presentationsToExpandedInstructions as presentationsToExpandedInstructions, FormatConverters_presentationsToPlaylist as presentationsToPlaylist };
195
338
  }
196
339
 
197
- declare abstract class ContentProvider {
340
+ interface FormatResolverOptions {
341
+ allowLossy?: boolean;
342
+ }
343
+ interface ResolvedFormatMeta {
344
+ isNative: boolean;
345
+ sourceFormat?: "playlist" | "presentations" | "instructions";
346
+ isLossy: boolean;
347
+ }
348
+ declare class FormatResolver {
349
+ private provider;
350
+ private options;
351
+ constructor(provider: IProvider, options?: FormatResolverOptions);
352
+ getProvider(): IProvider;
353
+ /** Extract the last segment from a path to use as fallback ID/title */
354
+ private getIdFromPath;
355
+ getPlaylist(path: string, auth?: ContentProviderAuthData | null): Promise<ContentFile[] | null>;
356
+ getPlaylistWithMeta(path: string, auth?: ContentProviderAuthData | null): Promise<{
357
+ data: ContentFile[] | null;
358
+ meta: ResolvedFormatMeta;
359
+ }>;
360
+ getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
361
+ getPresentationsWithMeta(path: string, auth?: ContentProviderAuthData | null): Promise<{
362
+ data: Plan | null;
363
+ meta: ResolvedFormatMeta;
364
+ }>;
365
+ getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
366
+ getInstructionsWithMeta(path: string, auth?: ContentProviderAuthData | null): Promise<{
367
+ data: Instructions | null;
368
+ meta: ResolvedFormatMeta;
369
+ }>;
370
+ }
371
+
372
+ declare class OAuthHelper {
373
+ generateCodeVerifier(): string;
374
+ generateCodeChallenge(verifier: string): Promise<string>;
375
+ buildAuthUrl(config: ContentProviderConfig, codeVerifier: string, redirectUri: string, state?: string): Promise<{
376
+ url: string;
377
+ challengeMethod: string;
378
+ }>;
379
+ exchangeCodeForTokens(config: ContentProviderConfig, providerId: string, code: string, codeVerifier: string, redirectUri: string): Promise<ContentProviderAuthData | null>;
380
+ }
381
+
382
+ declare class TokenHelper {
383
+ isAuthValid(auth: ContentProviderAuthData | null | undefined): boolean;
384
+ isTokenExpired(auth: ContentProviderAuthData): boolean;
385
+ refreshToken(config: ContentProviderConfig, auth: ContentProviderAuthData): Promise<ContentProviderAuthData | null>;
386
+ }
387
+
388
+ declare class DeviceFlowHelper {
389
+ supportsDeviceFlow(config: ContentProviderConfig): boolean;
390
+ initiateDeviceFlow(config: ContentProviderConfig): Promise<DeviceAuthorizationResponse | null>;
391
+ pollDeviceFlowToken(config: ContentProviderConfig, deviceCode: string): Promise<DeviceFlowPollResult>;
392
+ calculatePollDelay(baseInterval?: number, slowDownCount?: number): number;
393
+ }
394
+
395
+ declare class ApiHelper {
396
+ createAuthHeaders(auth: ContentProviderAuthData | null | undefined): Record<string, string> | null;
397
+ apiRequest<T>(config: ContentProviderConfig, _providerId: string, path: string, auth?: ContentProviderAuthData | null, method?: "GET" | "POST", body?: unknown): Promise<T | null>;
398
+ }
399
+
400
+ /**
401
+ * @deprecated Use IProvider interface instead. Providers should implement IProvider directly
402
+ * and use helper classes (OAuthHelper, TokenHelper, DeviceFlowHelper, ApiHelper) via composition.
403
+ * This class will be removed in a future version.
404
+ */
405
+ declare abstract class ContentProvider implements IContentProvider, IAuthProvider {
198
406
  abstract readonly id: string;
199
407
  abstract readonly name: string;
200
408
  abstract readonly logos: ProviderLogos;
201
409
  abstract readonly config: ContentProviderConfig;
202
- abstract browse(folder?: ContentFolder | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
203
- abstract getPresentations(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
204
- getPlaylist(folder: ContentFolder, auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
205
- getInstructions(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
206
- getExpandedInstructions(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
410
+ protected readonly oauthHelper: OAuthHelper;
411
+ protected readonly tokenHelper: TokenHelper;
412
+ protected readonly deviceFlowHelper: DeviceFlowHelper;
413
+ protected readonly apiHelper: ApiHelper;
414
+ abstract browse(path?: string | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
415
+ abstract getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
416
+ getPlaylist(path: string, auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
417
+ getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
207
418
  requiresAuth(): boolean;
208
419
  getCapabilities(): ProviderCapabilities;
209
420
  checkMediaLicense(_mediaId: string, _auth?: ContentProviderAuthData | null): Promise<MediaLicenseResult | null>;
210
421
  getAuthTypes(): AuthType[];
211
422
  isAuthValid(auth: ContentProviderAuthData | null | undefined): boolean;
212
423
  isTokenExpired(auth: ContentProviderAuthData): boolean;
424
+ refreshToken(auth: ContentProviderAuthData): Promise<ContentProviderAuthData | null>;
213
425
  generateCodeVerifier(): string;
214
426
  generateCodeChallenge(verifier: string): Promise<string>;
215
427
  buildAuthUrl(codeVerifier: string, redirectUri: string, state?: string): Promise<{
@@ -217,174 +429,252 @@ declare abstract class ContentProvider {
217
429
  challengeMethod: string;
218
430
  }>;
219
431
  exchangeCodeForTokens(code: string, codeVerifier: string, redirectUri: string): Promise<ContentProviderAuthData | null>;
220
- refreshToken(auth: ContentProviderAuthData): Promise<ContentProviderAuthData | null>;
221
432
  supportsDeviceFlow(): boolean;
222
433
  initiateDeviceFlow(): Promise<DeviceAuthorizationResponse | null>;
223
434
  pollDeviceFlowToken(deviceCode: string): Promise<DeviceFlowPollResult>;
224
435
  calculatePollDelay(baseInterval?: number, slowDownCount?: number): number;
225
436
  protected createAuthHeaders(auth: ContentProviderAuthData | null | undefined): Record<string, string> | null;
226
- protected apiRequest<T>(path: string, auth?: ContentProviderAuthData | null, method?: 'GET' | 'POST', body?: unknown): Promise<T | null>;
227
- protected createFolder(id: string, title: string, image?: string, providerData?: Record<string, unknown>): ContentFolder;
437
+ protected apiRequest<T>(path: string, auth?: ContentProviderAuthData | null, method?: "GET" | "POST", body?: unknown): Promise<T | null>;
438
+ protected createFolder(id: string, title: string, path: string, image?: string, isLeaf?: boolean): ContentFolder;
228
439
  protected createFile(id: string, title: string, url: string, options?: {
229
- mediaType?: 'video' | 'image';
440
+ mediaType?: "video" | "image";
230
441
  image?: string;
231
442
  muxPlaybackId?: string;
232
- providerData?: Record<string, unknown>;
443
+ seconds?: number;
444
+ loop?: boolean;
445
+ loopVideo?: boolean;
446
+ streamUrl?: string;
233
447
  }): ContentFile;
234
448
  }
235
449
 
236
- interface FormatResolverOptions {
237
- allowLossy?: boolean;
238
- }
239
- interface ResolvedFormatMeta {
240
- isNative: boolean;
241
- sourceFormat?: 'playlist' | 'presentations' | 'instructions' | 'expandedInstructions';
242
- isLossy: boolean;
243
- }
244
- declare class FormatResolver {
245
- private provider;
246
- private options;
247
- constructor(provider: ContentProvider, options?: FormatResolverOptions);
248
- getProvider(): ContentProvider;
249
- getPlaylist(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<ContentFile[] | null>;
250
- getPlaylistWithMeta(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<{
251
- data: ContentFile[] | null;
252
- meta: ResolvedFormatMeta;
253
- }>;
254
- getPresentations(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
255
- getPresentationsWithMeta(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<{
256
- data: Plan | null;
257
- meta: ResolvedFormatMeta;
258
- }>;
259
- getInstructions(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
260
- getInstructionsWithMeta(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<{
261
- data: Instructions | null;
262
- meta: ResolvedFormatMeta;
263
- }>;
264
- getExpandedInstructions(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
265
- getExpandedInstructionsWithMeta(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<{
266
- data: Instructions | null;
267
- meta: ResolvedFormatMeta;
268
- }>;
269
- }
270
-
271
- declare class APlayProvider extends ContentProvider {
450
+ /**
451
+ * APlay Provider
452
+ *
453
+ * Path structure (variable depth based on module products):
454
+ * /modules -> list modules
455
+ * /modules/{moduleId} -> list products OR libraries (depends on module)
456
+ * /modules/{moduleId}/products/{productId} -> list libraries (if module has multiple products)
457
+ * /modules/{moduleId}/products/{productId}/{libraryId} -> media files
458
+ * /modules/{moduleId}/libraries/{libraryId} -> media files (if module has 0-1 products)
459
+ */
460
+ declare class APlayProvider implements IProvider {
461
+ private readonly apiHelper;
462
+ private apiRequest;
272
463
  readonly id = "aplay";
273
464
  readonly name = "APlay";
274
465
  readonly logos: ProviderLogos;
275
466
  readonly config: ContentProviderConfig;
276
- getCapabilities(): ProviderCapabilities;
277
- browse(folder?: ContentFolder | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
278
- private getProductFolders;
467
+ readonly requiresAuth = true;
468
+ readonly authTypes: AuthType[];
469
+ readonly capabilities: ProviderCapabilities;
470
+ browse(path?: string | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
471
+ private getModules;
472
+ private getModuleContent;
279
473
  private getLibraryFolders;
280
474
  private getMediaFiles;
281
- getPresentations(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
475
+ getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
476
+ getPlaylist(path: string, auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
477
+ getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
282
478
  checkMediaLicense(mediaId: string, auth?: ContentProviderAuthData | null): Promise<MediaLicenseResult | null>;
283
479
  }
284
480
 
285
- declare class SignPresenterProvider extends ContentProvider {
481
+ /**
482
+ * SignPresenter Provider
483
+ *
484
+ * Path structure:
485
+ * /playlists -> list playlists
486
+ * /playlists/{playlistId} -> list messages (files)
487
+ */
488
+ declare class SignPresenterProvider implements IProvider {
489
+ private readonly apiHelper;
490
+ private apiRequest;
286
491
  readonly id = "signpresenter";
287
492
  readonly name = "SignPresenter";
288
493
  readonly logos: ProviderLogos;
289
494
  readonly config: ContentProviderConfig;
290
- getCapabilities(): ProviderCapabilities;
291
- browse(folder?: ContentFolder | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
495
+ readonly requiresAuth = true;
496
+ readonly authTypes: AuthType[];
497
+ readonly capabilities: ProviderCapabilities;
498
+ browse(path?: string | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
499
+ private getPlaylists;
292
500
  private getMessages;
293
- getPresentations(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
501
+ getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
502
+ getPlaylist(path: string, auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
503
+ getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
294
504
  }
295
505
 
296
- declare class LessonsChurchProvider extends ContentProvider {
506
+ /**
507
+ * LessonsChurch Provider
508
+ *
509
+ * Path structure:
510
+ * /lessons -> programs
511
+ * /lessons/{programId} -> studies
512
+ * /lessons/{programId}/{studyId} -> lessons
513
+ * /lessons/{programId}/{studyId}/{lessonId} -> venues
514
+ * /lessons/{programId}/{studyId}/{lessonId}/{venueId} -> playlist files
515
+ *
516
+ * /addons -> categories
517
+ * /addons/{category} -> add-on files
518
+ */
519
+ declare class LessonsChurchProvider implements IProvider {
297
520
  readonly id = "lessonschurch";
298
521
  readonly name = "Lessons.church";
299
522
  readonly logos: ProviderLogos;
300
523
  readonly config: ContentProviderConfig;
301
- requiresAuth(): boolean;
302
- getCapabilities(): ProviderCapabilities;
303
- getPlaylist(folder: ContentFolder, _auth?: ContentProviderAuthData | null, resolution?: number): Promise<ContentFile[] | null>;
304
- protected apiRequest<T>(path: string): Promise<T | null>;
305
- browse(folder?: ContentFolder | null, _auth?: ContentProviderAuthData | null, resolution?: number): Promise<ContentItem[]>;
524
+ readonly requiresAuth = false;
525
+ readonly authTypes: AuthType[];
526
+ readonly capabilities: ProviderCapabilities;
527
+ getPlaylist(path: string, _auth?: ContentProviderAuthData | null, resolution?: number): Promise<ContentFile[] | null>;
528
+ private apiRequest;
529
+ browse(path?: string | null, _auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
530
+ private browseLessons;
306
531
  private getPrograms;
307
532
  private getStudies;
308
533
  private getLessons;
309
534
  private getVenues;
310
535
  private getPlaylistFiles;
536
+ private browseAddOns;
311
537
  private getAddOnCategories;
312
538
  private getAddOnsByCategory;
313
539
  private convertAddOnToFile;
314
- getPresentations(folder: ContentFolder, _auth?: ContentProviderAuthData | null, resolution?: number): Promise<Plan | null>;
315
- getInstructions(folder: ContentFolder, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
316
- getExpandedInstructions(folder: ContentFolder, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
540
+ getPresentations(path: string, _auth?: ContentProviderAuthData | null): Promise<Plan | null>;
541
+ getInstructions(path: string, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
542
+ private normalizeItemType;
317
543
  private getEmbedUrl;
318
544
  private convertVenueToPlan;
319
545
  }
320
546
 
321
- declare class B1ChurchProvider extends ContentProvider {
547
+ declare class B1ChurchProvider implements IProvider {
548
+ private readonly apiHelper;
549
+ private apiRequest;
322
550
  readonly id = "b1church";
323
551
  readonly name = "B1.Church";
324
552
  readonly logos: ProviderLogos;
325
553
  readonly config: ContentProviderConfig;
326
554
  private appBase;
327
- requiresAuth(): boolean;
328
- getCapabilities(): ProviderCapabilities;
329
- buildAuthUrl(_codeVerifier: string, redirectUri: string, state?: string): Promise<{
555
+ readonly requiresAuth = true;
556
+ readonly authTypes: AuthType[];
557
+ readonly capabilities: ProviderCapabilities;
558
+ buildAuthUrl(codeVerifier: string, redirectUri: string, state?: string): Promise<{
330
559
  url: string;
331
560
  challengeMethod: string;
332
561
  }>;
562
+ exchangeCodeForTokensWithPKCE(code: string, redirectUri: string, codeVerifier: string): Promise<ContentProviderAuthData | null>;
333
563
  exchangeCodeForTokensWithSecret(code: string, redirectUri: string, clientSecret: string): Promise<ContentProviderAuthData | null>;
334
564
  refreshTokenWithSecret(authData: ContentProviderAuthData, clientSecret: string): Promise<ContentProviderAuthData | null>;
335
565
  initiateDeviceFlow(): Promise<DeviceAuthorizationResponse | null>;
336
566
  pollDeviceFlowToken(deviceCode: string): Promise<DeviceFlowPollResult>;
337
- browse(folder?: ContentFolder | null, authData?: ContentProviderAuthData | null): Promise<ContentItem[]>;
338
- getPresentations(folder: ContentFolder, authData?: ContentProviderAuthData | null): Promise<Plan | null>;
339
- getInstructions(folder: ContentFolder, authData?: ContentProviderAuthData | null): Promise<Instructions | null>;
340
- getPlaylist(folder: ContentFolder, authData?: ContentProviderAuthData | null): Promise<ContentFile[]>;
567
+ browse(path?: string | null, authData?: ContentProviderAuthData | null): Promise<ContentItem[]>;
568
+ getPresentations(path: string, authData?: ContentProviderAuthData | null): Promise<Plan | null>;
569
+ getInstructions(path: string, authData?: ContentProviderAuthData | null): Promise<Instructions | null>;
570
+ private processInstructionItems;
571
+ private findItemByPath;
572
+ private findPresentationByPath;
573
+ getPlaylist(path: string, authData?: ContentProviderAuthData | null, resolution?: number): Promise<ContentFile[] | null>;
341
574
  }
342
575
 
343
- declare class PlanningCenterProvider extends ContentProvider {
576
+ /**
577
+ * PlanningCenter Provider
578
+ *
579
+ * Path structure:
580
+ * /serviceTypes -> list service types
581
+ * /serviceTypes/{serviceTypeId} -> list plans
582
+ * /serviceTypes/{serviceTypeId}/{planId} -> plan items (leaf)
583
+ */
584
+ declare class PlanningCenterProvider implements IProvider {
585
+ private readonly apiHelper;
586
+ private apiRequest;
344
587
  readonly id = "planningcenter";
345
588
  readonly name = "Planning Center";
346
589
  readonly logos: ProviderLogos;
347
590
  readonly config: ContentProviderConfig;
348
591
  private readonly ONE_WEEK_MS;
349
- requiresAuth(): boolean;
350
- getCapabilities(): ProviderCapabilities;
351
- browse(folder?: ContentFolder | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
592
+ readonly requiresAuth = true;
593
+ readonly authTypes: AuthType[];
594
+ readonly capabilities: ProviderCapabilities;
595
+ browse(path?: string | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
596
+ private getServiceTypes;
352
597
  private getPlans;
353
598
  private getPlanItems;
354
- getPresentations(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
599
+ getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
355
600
  private convertToPresentation;
356
601
  private convertSongToPresentation;
357
602
  private convertMediaToPresentation;
358
603
  private formatDate;
604
+ getPlaylist(path: string, auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
605
+ getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
359
606
  }
360
607
 
361
- declare class BibleProjectProvider extends ContentProvider {
608
+ /**
609
+ * BibleProject Provider
610
+ *
611
+ * Path structure:
612
+ * / -> list collections
613
+ * /{collectionSlug} -> list videos in collection
614
+ * /{collectionSlug}/{videoId} -> single video
615
+ */
616
+ declare class BibleProjectProvider implements IProvider {
362
617
  readonly id = "bibleproject";
363
618
  readonly name = "The Bible Project";
364
619
  readonly logos: ProviderLogos;
365
620
  readonly config: ContentProviderConfig;
366
621
  private data;
367
- requiresAuth(): boolean;
368
- getCapabilities(): ProviderCapabilities;
369
- browse(folder?: ContentFolder | null, _auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
370
- getPresentations(folder: ContentFolder, _auth?: ContentProviderAuthData | null): Promise<Plan | null>;
371
- getPlaylist(folder: ContentFolder, _auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
622
+ readonly requiresAuth = false;
623
+ readonly authTypes: AuthType[];
624
+ readonly capabilities: ProviderCapabilities;
625
+ browse(path?: string | null, _auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
626
+ private getCollections;
627
+ private getLessonFolders;
628
+ private getVideoFile;
629
+ getPresentations(path: string, _auth?: ContentProviderAuthData | null): Promise<Plan | null>;
630
+ getPlaylist(path: string, _auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
631
+ getInstructions(path: string, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
632
+ private slugify;
633
+ }
634
+
635
+ /**
636
+ * HighVoltageKids Provider
637
+ *
638
+ * Path structure:
639
+ * / -> list collections (Elementary, Preschool)
640
+ * /{collectionSlug} -> list studies
641
+ * /{collectionSlug}/{studyId} -> list lessons
642
+ * /{collectionSlug}/{studyId}/{lessonId} -> lesson files (leaf)
643
+ */
644
+ declare class HighVoltageKidsProvider implements IProvider {
645
+ readonly id = "highvoltagekids";
646
+ readonly name = "High Voltage Kids";
647
+ readonly logos: ProviderLogos;
648
+ readonly config: ContentProviderConfig;
649
+ private data;
650
+ readonly requiresAuth = false;
651
+ readonly authTypes: AuthType[];
652
+ readonly capabilities: ProviderCapabilities;
653
+ browse(path?: string | null, _auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
654
+ private getCollections;
655
+ private getStudyFolders;
372
656
  private getLessonFolders;
657
+ private getLessonFiles;
658
+ getPresentations(path: string, _auth?: ContentProviderAuthData | null): Promise<Plan | null>;
659
+ getPlaylist(path: string, _auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
660
+ getInstructions(path: string, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
373
661
  private slugify;
662
+ private groupFilesIntoActions;
663
+ private getBaseName;
374
664
  }
375
665
 
376
666
  /**
377
667
  * Get a provider by ID.
378
668
  */
379
- declare function getProvider(providerId: string): ContentProvider | null;
669
+ declare function getProvider(providerId: string): IProvider | null;
380
670
  /**
381
671
  * Get all registered providers.
382
672
  */
383
- declare function getAllProviders(): ContentProvider[];
673
+ declare function getAllProviders(): IProvider[];
384
674
  /**
385
675
  * Register a custom provider.
386
676
  */
387
- declare function registerProvider(provider: ContentProvider): void;
677
+ declare function registerProvider(provider: IProvider): void;
388
678
  /**
389
679
  * Get provider configuration by ID (for backward compatibility).
390
680
  */
@@ -392,13 +682,14 @@ declare function getProviderConfig(providerId: string): ContentProviderConfig |
392
682
  /**
393
683
  * Get list of available providers with their info including logos and auth types.
394
684
  * Includes both implemented providers and coming soon providers.
685
+ * @param ids - Optional array of provider IDs to filter the results. If provided, only providers with matching IDs will be returned.
395
686
  */
396
- declare function getAvailableProviders(): ProviderInfo[];
687
+ declare function getAvailableProviders(ids?: string[]): ProviderInfo[];
397
688
 
398
689
  /**
399
690
  * @churchapps/content-provider-helper
400
691
  * Helper classes for interacting with third party content providers
401
692
  */
402
- declare const VERSION = "0.0.1";
693
+ declare const VERSION = "0.0.4";
403
694
 
404
- export { APlayProvider, type AuthType, B1ChurchProvider, BibleProjectProvider, type ContentFile, type ContentFolder, type ContentItem, ContentProvider, type ContentProviderAuthData, type ContentProviderConfig, type DeviceAuthorizationResponse, type DeviceFlowPollResult, type DeviceFlowState, type FeedActionInterface, type FeedFileInterface, type FeedSectionInterface, type FeedVenueInterface, FormatConverters, FormatResolver, type FormatResolverOptions, type InstructionItem, type Instructions, LessonsChurchProvider, type MediaLicenseResult, type MediaLicenseStatus, type Plan, type PlanPresentation, type PlanSection, PlanningCenterProvider, type ProviderCapabilities, type ProviderInfo, type ProviderLogos, type ResolvedFormatMeta, SignPresenterProvider, VERSION, type VenueActionInterface, type VenueActionsResponseInterface, type VenueSectionActionsInterface, collapseInstructions, detectMediaType, expandedInstructionsToPlaylist, expandedInstructionsToPresentations, getAllProviders, getAvailableProviders, getProvider, getProviderConfig, instructionsToPlaylist, instructionsToPresentations, isContentFile, isContentFolder, playlistToExpandedInstructions, playlistToInstructions, playlistToPresentations, presentationsToExpandedInstructions, presentationsToInstructions, presentationsToPlaylist, registerProvider };
695
+ export { APlayProvider, ApiHelper, type AuthType, B1ChurchProvider, BibleProjectProvider, type ContentFile, type ContentFolder, type ContentItem, ContentProvider, type ContentProviderAuthData, type ContentProviderConfig, DEFAULT_DURATION_CONFIG, type DeviceAuthorizationResponse, DeviceFlowHelper, type DeviceFlowPollResult, type DeviceFlowState, type DurationEstimationConfig, type FeedActionInterface, type FeedFileInterface, type FeedSectionInterface, type FeedVenueInterface, FormatConverters, FormatResolver, type FormatResolverOptions, HighVoltageKidsProvider, type IAuthProvider, type IContentProvider, type IProvider, type InstructionItem, type Instructions, LessonsChurchProvider, type MediaLicenseResult, type MediaLicenseStatus, OAuthHelper, type Plan, type PlanPresentation, type PlanSection, PlanningCenterProvider, type ProviderCapabilities, type ProviderInfo, type ProviderLogos, type ResolvedFormatMeta, SignPresenterProvider, TokenHelper, VERSION, type VenueActionInterface, type VenueActionsResponseInterface, type VenueSectionActionsInterface, appendToPath, buildPath, countWords, createFile, createFolder, detectMediaType, estimateDuration, estimateImageDuration, estimateTextDuration, expandedInstructionsToPlaylist, expandedInstructionsToPresentations, generatePath, getAllProviders, getAvailableProviders, getProvider, getProviderConfig, getSegment, instructionsToPlaylist, instructionsToPresentations, isContentFile, isContentFolder, navigateToPath, parsePath, playlistToExpandedInstructions, playlistToInstructions, playlistToPresentations, presentationsToExpandedInstructions, presentationsToPlaylist, registerProvider };