@churchapps/content-providers 0.4.1 → 0.5.1

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
@@ -19,7 +19,8 @@ interface ContentProviderConfig {
19
19
  scopes: string[];
20
20
  supportsDeviceFlow?: boolean;
21
21
  deviceAuthEndpoint?: string;
22
- endpoints: EndpointsConfig;
22
+ /** @deprecated URL templates now live in each provider; kept optional for type compatibility. */
23
+ endpoints?: EndpointsConfig;
23
24
  }
24
25
  interface DeviceAuthorizationResponse {
25
26
  device_code: string;
@@ -234,31 +235,54 @@ interface IProvider {
234
235
  checkMediaLicense?(mediaId: string, auth?: ContentProviderAuthData | null): Promise<MediaLicenseResult | null>;
235
236
  }
236
237
 
237
- /**
238
- * Navigate to a specific item using a dot-notation path.
239
- * Path format: "0.2.1" means items[0].children[2].children[1]
240
- */
238
+ /** Navigate to a specific item using dot-notation path (e.g. "0.2.1" → items[0].children[2].children[1]). */
241
239
  declare function navigateToPath(instructions: Instructions, path: string): InstructionItem | null;
242
240
 
241
+ declare class OAuthHelper {
242
+ generateCodeVerifier(): string;
243
+ generateCodeChallenge(verifier: string): Promise<string>;
244
+ buildAuthUrl(config: ContentProviderConfig, codeVerifier: string, redirectUri: string, state?: string): Promise<{
245
+ url: string;
246
+ challengeMethod: string;
247
+ }>;
248
+ exchangeCodeForTokens(config: ContentProviderConfig, _providerId: string, code: string, codeVerifier: string, redirectUri: string): Promise<ContentProviderAuthData | null>;
249
+ }
250
+
251
+ /** Map a raw OAuth token response to ContentProviderAuthData, stamping created_at. */
252
+ declare function toAuthData(data: Record<string, unknown>, fallbacks?: {
253
+ refreshToken?: string;
254
+ scope?: string;
255
+ }): ContentProviderAuthData;
243
256
  declare class TokenHelper {
244
257
  isAuthValid(auth: ContentProviderAuthData | null | undefined): boolean;
245
258
  isTokenExpired(auth: ContentProviderAuthData): boolean;
246
259
  refreshToken(config: ContentProviderConfig, auth: ContentProviderAuthData): Promise<ContentProviderAuthData | null>;
247
260
  }
248
261
 
249
- /**
250
- * APlay Provider
251
- *
252
- * Path structure (variable depth based on module products):
253
- * /modules -> list modules
254
- * /modules/{moduleId} -> list products OR libraries (depends on module)
255
- * /modules/{moduleId}/products/{productId} -> list libraries (if module has multiple products)
256
- * /modules/{moduleId}/products/{productId}/{libraryId} -> media files
257
- * /modules/{moduleId}/libraries/{libraryId} -> media files (if module has 0-1 products)
258
- */
259
- declare class APlayProvider implements IProvider {
262
+ declare class DeviceFlowHelper {
263
+ supportsDeviceFlow(config: ContentProviderConfig): boolean;
264
+ initiateDeviceFlow(config: ContentProviderConfig): Promise<DeviceAuthorizationResponse | null>;
265
+ pollDeviceFlowToken(config: ContentProviderConfig, deviceCode: string): Promise<DeviceFlowPollResult>;
266
+ calculatePollDelay(baseInterval?: number, slowDownCount?: number): number;
267
+ }
268
+
269
+ /** Base for providers; auth-flow methods are intentionally not here so consumers can feature-detect with `"method" in provider`. */
270
+ declare abstract class BaseProvider implements IProvider {
271
+ abstract readonly id: string;
272
+ abstract readonly name: string;
273
+ abstract readonly logos: ProviderLogos;
274
+ abstract readonly config: ContentProviderConfig;
275
+ abstract readonly requiresAuth: boolean;
276
+ abstract readonly authTypes: AuthType[];
277
+ abstract readonly capabilities: ProviderCapabilities;
260
278
  private readonly apiHelper;
261
- private apiRequest;
279
+ protected apiRequest<T>(path: string, auth?: ContentProviderAuthData | null, method?: "GET" | "POST", body?: unknown): Promise<T | null>;
280
+ abstract browse(path?: string | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
281
+ supportsDeviceFlow(): boolean;
282
+ }
283
+
284
+ /** APlay Provider: media curriculum library with OAuth 2.0 auth. */
285
+ declare class APlayProvider extends BaseProvider {
262
286
  readonly id = "aplay";
263
287
  readonly name = "APlay";
264
288
  readonly logos: ProviderLogos;
@@ -274,21 +298,12 @@ declare class APlayProvider implements IProvider {
274
298
  getPlaylist(path: string, auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
275
299
  getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
276
300
  checkMediaLicense(mediaId: string, auth?: ContentProviderAuthData | null): Promise<MediaLicenseResult | null>;
277
- supportsDeviceFlow(): boolean;
278
301
  }
279
302
 
280
- /**
281
- * SignPresenter Provider
282
- *
283
- * Path structure:
284
- * /playlists -> list playlists
285
- * /playlists/{playlistId} -> list messages (files)
286
- */
287
- declare class SignPresenterProvider implements IProvider {
288
- private readonly apiHelper;
303
+ /** Browses SignPresenter playlists → messages (files). */
304
+ declare class SignPresenterProvider extends BaseProvider {
289
305
  private readonly oauthHelper;
290
306
  private readonly deviceFlowHelper;
291
- private apiRequest;
292
307
  readonly id = "signpresenter";
293
308
  readonly name = "SignPresenter";
294
309
  readonly logos: ProviderLogos;
@@ -297,11 +312,11 @@ declare class SignPresenterProvider implements IProvider {
297
312
  readonly authTypes: AuthType[];
298
313
  readonly capabilities: ProviderCapabilities;
299
314
  browse(path?: string | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
315
+ private extractList;
300
316
  private getPlaylists;
301
317
  private getMessages;
302
318
  getPlaylist(path: string, auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
303
319
  getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
304
- supportsDeviceFlow(): boolean;
305
320
  generateCodeVerifier(): string;
306
321
  buildAuthUrl(codeVerifier: string, redirectUri: string, state?: string): Promise<{
307
322
  url: string;
@@ -313,20 +328,11 @@ declare class SignPresenterProvider implements IProvider {
313
328
  }
314
329
 
315
330
  /**
316
- * LessonsChurch Provider
317
- *
318
- * Path structure:
319
- * /lessons -> programs
320
- * /lessons/{programId} -> studies
321
- * /lessons/{programId}/{studyId} -> lessons
322
- * /lessons/{programId}/{studyId}/{lessonId} -> venues
323
- * /lessons/{programId}/{studyId}/{lessonId}/{venueId} -> playlist files
324
- *
325
- * /addons -> categories
326
- * /addons/{category} -> individual add-ons
327
- * /addons/{category}/{addOnId} -> single add-on file
331
+ * Path: /lessons → programs, /lessons/{id} → studies, /lessons/{id}/{id} → lessons,
332
+ * /lessons/{id}/{id}/{id} → venues, /lessons/{id}/{id}/{id}/{id} → files;
333
+ * /addons → categories, /addons/{cat} → items, /addons/{cat}/{id} → file.
328
334
  */
329
- declare class LessonsChurchProvider implements IProvider {
335
+ declare class LessonsChurchProvider extends BaseProvider {
330
336
  readonly id = "lessonschurch";
331
337
  readonly name = "Lessons.church";
332
338
  readonly logos: ProviderLogos;
@@ -347,14 +353,11 @@ declare class LessonsChurchProvider implements IProvider {
347
353
  private getAddOnCategories;
348
354
  private getAddOnsByCategory;
349
355
  getInstructions(path: string, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
350
- supportsDeviceFlow(): boolean;
351
356
  }
352
357
 
353
- declare class B1ChurchProvider implements IProvider {
354
- private readonly apiHelper;
358
+ declare class B1ChurchProvider extends BaseProvider {
355
359
  private readonly deviceFlowHelper;
356
360
  private readonly externalContentCache;
357
- private apiRequest;
358
361
  readonly id = "b1church";
359
362
  readonly name = "B1.Church";
360
363
  readonly logos: ProviderLogos;
@@ -382,12 +385,10 @@ declare class B1ChurchProvider implements IProvider {
382
385
  private findItemByPath;
383
386
  private findPresentationByPath;
384
387
  getPlaylist(path: string, authData?: ContentProviderAuthData | null, resolution?: number): Promise<ContentFile[] | null>;
385
- supportsDeviceFlow(): boolean;
386
388
  }
387
389
 
388
- declare class DropboxProvider implements IProvider {
390
+ declare class DropboxProvider extends BaseProvider {
389
391
  private readonly oauthHelper;
390
- private readonly apiHelper;
391
392
  readonly id = "dropbox";
392
393
  readonly name = "Dropbox";
393
394
  readonly logos: ProviderLogos;
@@ -403,9 +404,9 @@ declare class DropboxProvider implements IProvider {
403
404
  private resolveFileLinks;
404
405
  private checkIsLeaf;
405
406
  browse(path?: string | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
407
+ private resolveMediaFiles;
406
408
  getPlaylist(path: string, auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
407
409
  getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
408
- supportsDeviceFlow(): boolean;
409
410
  generateCodeVerifier(): string;
410
411
  buildAuthUrl(codeVerifier: string, redirectUri: string, state?: string): Promise<{
411
412
  url: string;
@@ -416,22 +417,12 @@ declare class DropboxProvider implements IProvider {
416
417
  exchangeCodeForTokens(code: string, codeVerifier: string, redirectUri: string): Promise<ContentProviderAuthData | null>;
417
418
  }
418
419
 
419
- /**
420
- * PlanningCenter Provider
421
- *
422
- * Path structure:
423
- * /serviceTypes -> list service types
424
- * /serviceTypes/{serviceTypeId} -> list plans
425
- * /serviceTypes/{serviceTypeId}/{planId} -> plan items (leaf)
426
- */
427
- declare class PlanningCenterProvider implements IProvider {
428
- private readonly apiHelper;
429
- private apiRequest;
420
+ /** Browses Planning Center service types → plans → items. */
421
+ declare class PlanningCenterProvider extends BaseProvider {
430
422
  readonly id = "planningcenter";
431
423
  readonly name = "Planning Center";
432
424
  readonly logos: ProviderLogos;
433
425
  readonly config: ContentProviderConfig;
434
- private readonly ONE_WEEK_MS;
435
426
  readonly requiresAuth = true;
436
427
  readonly authTypes: AuthType[];
437
428
  readonly capabilities: ProviderCapabilities;
@@ -439,46 +430,32 @@ declare class PlanningCenterProvider implements IProvider {
439
430
  private getServiceTypes;
440
431
  private getPlans;
441
432
  private getPlanItems;
442
- supportsDeviceFlow(): boolean;
443
433
  }
444
434
 
445
- /**
446
- * BibleProject Provider
447
- *
448
- * Path structure:
449
- * / -> list collections
450
- * /{collectionSlug} -> list videos in collection
451
- * /{collectionSlug}/{videoId} -> single video
452
- */
453
- declare class BibleProjectProvider implements IProvider {
435
+ /** BibleProject Provider: Bible education video content. */
436
+ declare class BibleProjectProvider extends BaseProvider {
454
437
  readonly id = "bibleproject";
455
438
  readonly name = "The Bible Project";
456
439
  readonly logos: ProviderLogos;
457
440
  readonly config: ContentProviderConfig;
458
- private data;
459
- private getMuxThumbnail;
460
441
  readonly requiresAuth = false;
461
442
  readonly authTypes: AuthType[];
462
443
  readonly capabilities: ProviderCapabilities;
444
+ private data;
445
+ private getMuxThumbnail;
446
+ private videoToFile;
447
+ /** Resolve a path to its collection and files: all videos at depth 1, a single video at depth 2. */
448
+ private resolve;
463
449
  browse(path?: string | null, _auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
464
- private getCollections;
465
- private getLessonFolders;
466
- private getVideoFile;
467
450
  getPlaylist(path: string, _auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
468
451
  getInstructions(path: string, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
469
- supportsDeviceFlow(): boolean;
470
452
  }
471
453
 
472
454
  /**
473
- * HighVoltageKids Provider
474
- *
475
- * Path structure:
476
- * / -> list collections (Elementary, Preschool)
477
- * /{collectionSlug} -> list studies
478
- * /{collectionSlug}/{studyId} -> list lessons
479
- * /{collectionSlug}/{studyId}/{lessonId} -> lesson files (leaf)
455
+ * Path structure: / → collections, /{collection} → studies, /{collection}/{study} → lessons,
456
+ * /{collection}/{study}/{lesson} → files (leaf).
480
457
  */
481
- declare class HighVoltageKidsProvider implements IProvider {
458
+ declare class HighVoltageKidsProvider extends BaseProvider {
482
459
  readonly id = "highvoltagekids";
483
460
  readonly name = "High Voltage Kids";
484
461
  readonly logos: ProviderLogos;
@@ -490,21 +467,13 @@ declare class HighVoltageKidsProvider implements IProvider {
490
467
  browse(path?: string | null, _auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
491
468
  getPlaylist(path: string, _auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
492
469
  getInstructions(path: string, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
493
- supportsDeviceFlow(): boolean;
494
470
  }
495
471
 
496
472
  /**
497
- * Jesus Film Project Provider
498
- *
499
- * Uses the public Arclight API to browse video content from jesusfilm.org.
500
- *
501
- * Path structure:
502
- * / -> categories (Feature Films, Series, Collections)
503
- * /{category} -> list items in category
504
- * /{category}/{id} -> single video (feature film) or container children (series/collection)
505
- * /{category}/{...containerIds}/{id} -> nested containers or single video (supports arbitrary depth)
473
+ * Path: / → categories, /{category} → items, /{category}/{id} → video or children,
474
+ * /{category}/{...ids}/{id} → nested containers or video.
506
475
  */
507
- declare class JesusFilmProvider implements IProvider {
476
+ declare class JesusFilmProvider extends BaseProvider {
508
477
  readonly id = "jesusfilm";
509
478
  readonly name = "Jesus Film Project";
510
479
  readonly logos: ProviderLogos;
@@ -513,36 +482,24 @@ declare class JesusFilmProvider implements IProvider {
513
482
  readonly authTypes: AuthType[];
514
483
  readonly capabilities: ProviderCapabilities;
515
484
  private fetchApi;
485
+ private fetchCategoryComponents;
516
486
  browse(path?: string | null, _auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
517
487
  private getCategories;
518
488
  private getItemsInCategory;
519
489
  private getVideoFile;
520
490
  private getContainerChildren;
521
491
  private extractMuxPlaybackId;
522
- getPlaylist(path: string, _auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
523
492
  private fetchVideoFiles;
524
493
  private fetchContainerVideoFiles;
494
+ getPlaylist(path: string, _auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
525
495
  private buildVideoInstructions;
526
496
  private buildContainerInstructions;
527
497
  getInstructions(path: string, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
528
- supportsDeviceFlow(): boolean;
529
498
  }
530
499
 
531
- /**
532
- * CBN Provider
533
- *
534
- * Faith-based kids' video curriculum. Auth uses OAuth 2.0 Device Flow.
535
- *
536
- * Path structure:
537
- * / -> single "Catalog" root folder
538
- * /catalog -> course folders (GET /catalog)
539
- * /catalog/{courseId} -> lesson folders (GET /catalog/{courseId})
540
- * /catalog/{courseId}/{lessonId} -> video files (GET /lesson-playlist/{lessonId})
541
- */
542
- declare class CbnProvider implements IProvider {
543
- private readonly apiHelper;
500
+ /** Faith-based kids' video curriculum with OAuth 2.0 Device Flow auth. */
501
+ declare class CbnProvider extends BaseProvider {
544
502
  private readonly deviceFlowHelper;
545
- private apiRequest;
546
503
  readonly id = "cbn";
547
504
  readonly name = "CBN";
548
505
  readonly logos: ProviderLogos;
@@ -551,38 +508,16 @@ declare class CbnProvider implements IProvider {
551
508
  readonly authTypes: AuthType[];
552
509
  readonly capabilities: ProviderCapabilities;
553
510
  browse(path?: string | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
554
- private getCourses;
555
- private getLessons;
556
- private getVideos;
557
- private fetchPlaylist;
511
+ /** Resolve a leaf path (/catalog/{courseId}/{lessonId}) to its playlist files. */
512
+ private resolveFiles;
558
513
  getPlaylist(path: string, auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
559
514
  getInstructions(path: string, auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
560
- supportsDeviceFlow(): boolean;
561
515
  initiateDeviceFlow(): Promise<DeviceAuthorizationResponse | null>;
562
516
  pollDeviceFlowToken(deviceCode: string): Promise<DeviceFlowPollResult>;
563
517
  }
564
518
 
565
- /**
566
- * Life.Church Provider
567
- *
568
- * Browses the Life.Church kids curriculum from a static catalog generated by
569
- * scripts/scrape-lifechurch.ts (sourced from life.church/media/* + the
570
- * open.life.church weekly calendar).
571
- *
572
- * Path structure:
573
- * / -> "scheduled" folder + 4 series
574
- * /scheduled -> this Sunday's scheduled lessons (one per age group)
575
- * /scheduled/{scheduledId} -> single video file (leaf)
576
- * /{seriesId} -> units within the series
577
- * /{seriesId}/{unitId} -> lessons within the unit
578
- * /{seriesId}/{unitId}/{lessonId} -> single video file (leaf)
579
- *
580
- * Video delivery: ContentFile.url is a direct mp4 on Life.Church's CDN
581
- * (https://d.life.church/.../{slug}_720p.mp4). Public, no auth, supports
582
- * range requests — plays in any standard mp4 player including
583
- * react-native-video.
584
- */
585
- declare class LifeChurchProvider implements IProvider {
519
+ /** Browses Life.Church's kids curriculum from a static catalog (series → units → lessons), plus a weekly scheduled lesson index. */
520
+ declare class LifeChurchProvider extends BaseProvider {
586
521
  readonly id = "lifechurch";
587
522
  readonly name = "Life.Church";
588
523
  readonly logos: ProviderLogos;
@@ -592,12 +527,12 @@ declare class LifeChurchProvider implements IProvider {
592
527
  readonly capabilities: ProviderCapabilities;
593
528
  private data;
594
529
  private findSeries;
595
- private findUnit;
596
- private findLesson;
597
530
  private findScheduled;
598
531
  /** Resolve a scheduled-lesson reference to the underlying lesson + unit + series. */
599
532
  private resolveScheduled;
600
533
  private lessonToFile;
534
+ /** Resolve a leaf-or-unit path to its files plus the name/section used for instructions. */
535
+ private resolve;
601
536
  browse(path?: string | null, _auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
602
537
  getPlaylist(path: string, _auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
603
538
  getInstructions(path: string, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
@@ -605,7 +540,6 @@ declare class LifeChurchProvider implements IProvider {
605
540
  * Returns the most recent scheduled lesson from data.scheduledLessons.
606
541
  */
607
542
  getCurrentPlan(_auth?: ContentProviderAuthData | null): Promise<CurrentPlan | null>;
608
- supportsDeviceFlow(): boolean;
609
543
  }
610
544
 
611
545
  declare function getProvider(providerId: string): IProvider | null;
@@ -616,11 +550,7 @@ declare function registerProvider(provider: IProvider): void;
616
550
  * Get provider configuration by ID (for backward compatibility).
617
551
  */
618
552
  declare function getProviderConfig(providerId: string): ContentProviderConfig | null;
619
- /**
620
- * Get list of available providers with their info including logos and auth types.
621
- * Includes both implemented providers and coming soon providers.
622
- * @param ids - Optional array of provider IDs to filter the results. If provided, only providers with matching IDs will be returned.
623
- */
553
+ /** Get available providers (implemented and coming soon), optionally filtered by IDs. */
624
554
  declare function getAvailableProviders(ids?: string[]): ProviderInfo[];
625
555
 
626
556
  /**
@@ -629,4 +559,4 @@ declare function getAvailableProviders(ids?: string[]): ProviderInfo[];
629
559
  */
630
560
  declare const VERSION: string;
631
561
 
632
- export { APlayProvider, type AuthType, B1ChurchProvider, BibleProjectProvider, CbnProvider, type ContentFile, type ContentFolder, type ContentItem, type ContentProviderAuthData, type ContentProviderConfig, type CurrentPlan, type DeviceAuthorizationResponse, type DeviceFlowPollResult, type DeviceFlowState, DropboxProvider, type EndpointValue, type EndpointsConfig, type FeedActionInterface, type FeedFileInterface, type FeedSectionInterface, type FeedVenueInterface, HighVoltageKidsProvider, type IProvider, type InstructionItem, type Instructions, JesusFilmProvider, LessonsChurchProvider, LifeChurchProvider, type MediaLicenseResult, type MediaLicenseStatus, type MessageFileInterface, type Plan, type PlanPresentation, type PlanSection, type PlanTimelineItem, PlanningCenterProvider, type ProviderCapabilities, type ProviderInfo, type ProviderLogos, SignPresenterProvider, TokenHelper, VERSION, type VenueActionInterface, type VenueActionsResponseInterface, type VenueSectionActionsInterface, getAllProviders, getAvailableProviders, getProvider, getProviderConfig, isContentFile, isContentFolder, navigateToPath, registerProvider };
562
+ export { APlayProvider, type AuthType, B1ChurchProvider, BaseProvider, BibleProjectProvider, CbnProvider, type ContentFile, type ContentFolder, type ContentItem, type ContentProviderAuthData, type ContentProviderConfig, type CurrentPlan, type DeviceAuthorizationResponse, DeviceFlowHelper, type DeviceFlowPollResult, type DeviceFlowState, DropboxProvider, type EndpointValue, type EndpointsConfig, type FeedActionInterface, type FeedFileInterface, type FeedSectionInterface, type FeedVenueInterface, HighVoltageKidsProvider, type IProvider, type InstructionItem, type Instructions, JesusFilmProvider, LessonsChurchProvider, LifeChurchProvider, type MediaLicenseResult, type MediaLicenseStatus, type MessageFileInterface, OAuthHelper, type Plan, type PlanPresentation, type PlanSection, type PlanTimelineItem, PlanningCenterProvider, type ProviderCapabilities, type ProviderInfo, type ProviderLogos, SignPresenterProvider, TokenHelper, VERSION, type VenueActionInterface, type VenueActionsResponseInterface, type VenueSectionActionsInterface, getAllProviders, getAvailableProviders, getProvider, getProviderConfig, isContentFile, isContentFolder, navigateToPath, registerProvider, toAuthData };