@churchapps/content-provider-helper 0.0.2 → 0.0.3

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