@churchapps/content-provider-helper 0.0.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.
@@ -0,0 +1,733 @@
1
+ /**
2
+ * OAuth token data returned after successful authentication.
3
+ */
4
+ interface ContentProviderAuthData {
5
+ /** The access token for API requests */
6
+ access_token: string;
7
+ /** The refresh token for obtaining new access tokens */
8
+ refresh_token: string;
9
+ /** Token type, typically "Bearer" */
10
+ token_type: string;
11
+ /** Unix timestamp (seconds) when the token was created */
12
+ created_at: number;
13
+ /** Token lifetime in seconds */
14
+ expires_in: number;
15
+ /** Space-separated list of granted scopes */
16
+ scope: string;
17
+ }
18
+ /**
19
+ * Configuration for a content provider's API and OAuth settings.
20
+ */
21
+ interface ContentProviderConfig {
22
+ /** Unique identifier for the provider */
23
+ id: string;
24
+ /** Display name of the provider */
25
+ name: string;
26
+ /** Base URL for API requests */
27
+ apiBase: string;
28
+ /** Base URL for OAuth endpoints */
29
+ oauthBase: string;
30
+ /** OAuth client ID */
31
+ clientId: string;
32
+ /** OAuth scopes to request */
33
+ scopes: string[];
34
+ /** Whether the provider supports device flow authentication */
35
+ supportsDeviceFlow?: boolean;
36
+ /** Endpoint path for device authorization (relative to oauthBase) */
37
+ deviceAuthEndpoint?: string;
38
+ /** API endpoint paths - can be static strings or functions that generate paths */
39
+ endpoints: Record<string, string | ((...args: string[]) => string)>;
40
+ }
41
+ /**
42
+ * Response from the device authorization endpoint (RFC 8628).
43
+ */
44
+ interface DeviceAuthorizationResponse {
45
+ /** Device verification code for polling */
46
+ device_code: string;
47
+ /** User code to display for manual entry */
48
+ user_code: string;
49
+ /** URL where user should authenticate */
50
+ verification_uri: string;
51
+ /** Complete URL with user code pre-filled */
52
+ verification_uri_complete?: string;
53
+ /** Seconds until the codes expire */
54
+ expires_in: number;
55
+ /** Minimum polling interval in seconds */
56
+ interval?: number;
57
+ }
58
+ /**
59
+ * Current state of a device flow authentication process.
60
+ */
61
+ interface DeviceFlowState {
62
+ /**
63
+ * Current status of the device flow.
64
+ * - `loading`: Initiating device flow
65
+ * - `awaiting_user`: Waiting for user to authenticate
66
+ * - `polling`: Polling for token
67
+ * - `success`: Authentication completed
68
+ * - `error`: An error occurred
69
+ * - `expired`: Device code expired
70
+ */
71
+ status: 'loading' | 'awaiting_user' | 'polling' | 'success' | 'error' | 'expired';
72
+ /** Device authorization response data */
73
+ deviceAuth?: DeviceAuthorizationResponse;
74
+ /** Error message if status is 'error' */
75
+ error?: string;
76
+ /** Number of poll attempts made */
77
+ pollCount?: number;
78
+ }
79
+ /**
80
+ * Authentication type supported by a provider.
81
+ * - `none`: No authentication required (public API)
82
+ * - `oauth_pkce`: OAuth 2.0 with PKCE
83
+ * - `device_flow`: OAuth 2.0 Device Authorization Flow (RFC 8628)
84
+ * - `form_login`: Form-based login (username/password) with session cookies
85
+ */
86
+ type AuthType = 'none' | 'oauth_pkce' | 'device_flow' | 'form_login';
87
+ /**
88
+ * Provider logo URLs for light and dark themes.
89
+ */
90
+ interface ProviderLogos {
91
+ /** Logo URL for light theme backgrounds */
92
+ light: string;
93
+ /** Logo URL for dark theme backgrounds */
94
+ dark: string;
95
+ }
96
+ /**
97
+ * Information about a content provider.
98
+ */
99
+ interface ProviderInfo {
100
+ /** Unique identifier for the provider */
101
+ id: string;
102
+ /** Display name of the provider */
103
+ name: string;
104
+ /** Provider logos */
105
+ logos: ProviderLogos;
106
+ /** Whether the provider is fully implemented */
107
+ implemented: boolean;
108
+ /** Whether the provider requires authentication */
109
+ requiresAuth: boolean;
110
+ /** Supported authentication types */
111
+ authTypes: AuthType[];
112
+ /** Provider capabilities */
113
+ capabilities: ProviderCapabilities;
114
+ }
115
+ /**
116
+ * A folder in the content hierarchy. Can be navigated into to retrieve child items.
117
+ */
118
+ interface ContentFolder {
119
+ /** Discriminator for type narrowing. Always `'folder'` */
120
+ type: 'folder';
121
+ /** Unique identifier for this folder */
122
+ id: string;
123
+ /** Display title */
124
+ title: string;
125
+ /** Optional thumbnail/cover image URL */
126
+ image?: string;
127
+ /** Provider-specific data for navigation (e.g., level, parentId) */
128
+ providerData?: Record<string, unknown>;
129
+ }
130
+ /**
131
+ * A playable media file (video or image).
132
+ */
133
+ interface ContentFile {
134
+ /** Discriminator for type narrowing. Always `'file'` */
135
+ type: 'file';
136
+ /** Unique identifier for this file */
137
+ id: string;
138
+ /** Display title */
139
+ title: string;
140
+ /**
141
+ * Media type of the file.
142
+ * - `video`: Video content (.mp4, .webm, .m3u8, etc.)
143
+ * - `image`: Image content (.jpg, .png, etc.)
144
+ */
145
+ mediaType: 'video' | 'image';
146
+ /** Optional preview/cover image URL */
147
+ image?: string;
148
+ /** URL to the media file */
149
+ url: string;
150
+ /**
151
+ * Optional URL for embedded preview/player.
152
+ * - For iframe-based providers (e.g., Lessons.church): an embed URL
153
+ * - For direct media providers: same as url, or omitted to use url directly
154
+ */
155
+ embedUrl?: string;
156
+ /** Mux playback ID for Mux-hosted videos */
157
+ muxPlaybackId?: string;
158
+ /** Decryption key for encrypted media (provider-specific) */
159
+ decryptionKey?: string;
160
+ /** Provider-specific media ID for tracking/licensing */
161
+ mediaId?: string;
162
+ /** URL to ping after 30+ seconds of playback (licensing requirement) */
163
+ pingbackUrl?: string;
164
+ /** Provider-specific data (e.g., duration, loop settings) */
165
+ providerData?: Record<string, unknown>;
166
+ }
167
+ /**
168
+ * A content item - either a folder or a file.
169
+ */
170
+ type ContentItem = ContentFolder | ContentFile;
171
+ /**
172
+ * Type guard to check if a ContentItem is a ContentFolder.
173
+ */
174
+ declare function isContentFolder(item: ContentItem): item is ContentFolder;
175
+ /**
176
+ * Type guard to check if a ContentItem is a ContentFile.
177
+ */
178
+ declare function isContentFile(item: ContentItem): item is ContentFile;
179
+ /**
180
+ * Result from polling the device flow token endpoint.
181
+ * - `ContentProviderAuthData`: Authentication succeeded
182
+ * - `{ error, shouldSlowDown }`: Still pending or should slow down
183
+ * - `null`: Authentication failed or expired
184
+ */
185
+ type DeviceFlowPollResult = ContentProviderAuthData | {
186
+ error: string;
187
+ shouldSlowDown?: boolean;
188
+ } | null;
189
+ /**
190
+ * A presentation within a plan section (e.g., a song, video, or activity).
191
+ */
192
+ interface PlanPresentation {
193
+ /** Unique identifier */
194
+ id: string;
195
+ /** Display name */
196
+ name: string;
197
+ /**
198
+ * Type of action/presentation.
199
+ * - `play`: Main playable content
200
+ * - `add-on`: Supplementary content
201
+ * - `other`: Non-playable item (song lyrics, notes, etc.)
202
+ */
203
+ actionType: 'play' | 'add-on' | 'other';
204
+ /** Media files associated with this presentation */
205
+ files: ContentFile[];
206
+ }
207
+ /**
208
+ * A section within a plan containing multiple presentations.
209
+ */
210
+ interface PlanSection {
211
+ /** Unique identifier */
212
+ id: string;
213
+ /** Section name (e.g., "Worship", "Message", "Closing") */
214
+ name: string;
215
+ /** Presentations within this section */
216
+ presentations: PlanPresentation[];
217
+ }
218
+ /**
219
+ * A complete plan/service with sections and presentations.
220
+ * Returned by `getPresentations()`.
221
+ */
222
+ interface Plan {
223
+ /** Unique identifier */
224
+ id: string;
225
+ /** Plan name */
226
+ name: string;
227
+ /** Optional description */
228
+ description?: string;
229
+ /** Optional cover image URL */
230
+ image?: string;
231
+ /** Ordered sections in the plan */
232
+ sections: PlanSection[];
233
+ /** Flat list of all files in the plan */
234
+ allFiles: ContentFile[];
235
+ }
236
+ /**
237
+ * A file within a venue feed.
238
+ */
239
+ interface FeedFileInterface {
240
+ id?: string;
241
+ name?: string;
242
+ url?: string;
243
+ streamUrl?: string;
244
+ seconds?: number;
245
+ fileType?: string;
246
+ }
247
+ /**
248
+ * An action within a venue feed section.
249
+ */
250
+ interface FeedActionInterface {
251
+ id?: string;
252
+ actionType?: string;
253
+ content?: string;
254
+ files?: FeedFileInterface[];
255
+ }
256
+ /**
257
+ * A section within a venue feed.
258
+ */
259
+ interface FeedSectionInterface {
260
+ id?: string;
261
+ name?: string;
262
+ actions?: FeedActionInterface[];
263
+ }
264
+ /**
265
+ * Complete venue feed data from Lessons.church API.
266
+ */
267
+ interface FeedVenueInterface {
268
+ id?: string;
269
+ lessonId?: string;
270
+ name?: string;
271
+ lessonName?: string;
272
+ lessonDescription?: string;
273
+ lessonImage?: string;
274
+ sections?: FeedSectionInterface[];
275
+ }
276
+ /**
277
+ * An item in the instruction hierarchy.
278
+ */
279
+ interface InstructionItem {
280
+ /** Unique identifier */
281
+ id?: string;
282
+ /** Type of instruction item */
283
+ itemType?: string;
284
+ /** ID of related content */
285
+ relatedId?: string;
286
+ /** Display label */
287
+ label?: string;
288
+ /** Description or notes */
289
+ description?: string;
290
+ /** Duration in seconds */
291
+ seconds?: number;
292
+ /** Child instruction items */
293
+ children?: InstructionItem[];
294
+ /** URL for embedded content viewer */
295
+ embedUrl?: string;
296
+ }
297
+ /**
298
+ * Instructions/run sheet for a venue.
299
+ * Returned by `getInstructions()` and `getExpandedInstructions()`.
300
+ */
301
+ interface Instructions {
302
+ /** Name of the venue */
303
+ venueName?: string;
304
+ /** Hierarchical list of instruction items */
305
+ items: InstructionItem[];
306
+ }
307
+ /**
308
+ * An action within a venue section.
309
+ */
310
+ interface VenueActionInterface {
311
+ id?: string;
312
+ name?: string;
313
+ actionType?: string;
314
+ seconds?: number;
315
+ }
316
+ /**
317
+ * A section with its actions.
318
+ */
319
+ interface VenueSectionActionsInterface {
320
+ id?: string;
321
+ name?: string;
322
+ actions?: VenueActionInterface[];
323
+ }
324
+ /**
325
+ * Response containing venue actions organized by section.
326
+ */
327
+ interface VenueActionsResponseInterface {
328
+ venueName?: string;
329
+ sections?: VenueSectionActionsInterface[];
330
+ }
331
+ /**
332
+ * Capabilities supported by a content provider.
333
+ */
334
+ interface ProviderCapabilities {
335
+ /** Whether the provider supports browsing content hierarchy */
336
+ browse: boolean;
337
+ /** Whether `getPresentations()` returns structured plan data */
338
+ presentations: boolean;
339
+ /** Whether `getPlaylist()` returns a flat list of media files */
340
+ playlist: boolean;
341
+ /** Whether `getInstructions()` returns instruction data */
342
+ instructions: boolean;
343
+ /** Whether `getExpandedInstructions()` returns expanded instruction data */
344
+ expandedInstructions: boolean;
345
+ /** Whether `checkMediaLicense()` returns license information */
346
+ mediaLicensing: boolean;
347
+ }
348
+ /**
349
+ * License status for media content.
350
+ */
351
+ type MediaLicenseStatus = 'valid' | 'expired' | 'not_licensed' | 'unknown';
352
+ /**
353
+ * Response from `checkMediaLicense()`.
354
+ */
355
+ interface MediaLicenseResult {
356
+ /** The media ID that was checked */
357
+ mediaId: string;
358
+ /** Current license status */
359
+ status: MediaLicenseStatus;
360
+ /** Human-readable message about the license */
361
+ message?: string;
362
+ /** When the license expires (ISO 8601 string or Unix timestamp) */
363
+ expiresAt?: string | number;
364
+ }
365
+
366
+ /**
367
+ * Detects whether a URL points to a video or image file.
368
+ * @param url - The URL to check
369
+ * @param explicitType - Optional explicit type from API response (e.g., 'video', 'image')
370
+ * @returns 'video' or 'image'
371
+ */
372
+ declare function detectMediaType(url: string, explicitType?: string): 'video' | 'image';
373
+
374
+ /**
375
+ * Abstract base class for content providers.
376
+ * Extend this class to create a custom provider.
377
+ *
378
+ * ## Main Methods (for consumers)
379
+ *
380
+ * ### Content Browsing
381
+ * - `browse(folder?, auth?)` - Browse content hierarchy (root if no folder, or folder contents)
382
+ *
383
+ * ### Structured Data
384
+ * - `getPresentations(folder, auth?)` - Get structured plan with sections and presentations
385
+ * - `getInstructions(folder, auth?)` - Get instruction/run sheet data
386
+ * - `getExpandedInstructions(folder, auth?)` - Get expanded instructions with actions
387
+ *
388
+ * ### Provider Info
389
+ * - `requiresAuth()` - Whether authentication is needed
390
+ * - `getCapabilities()` - What features the provider supports
391
+ * - `getAuthTypes()` - Supported authentication methods
392
+ *
393
+ * ### Authentication (OAuth 2.0 PKCE)
394
+ * - `buildAuthUrl(codeVerifier, redirectUri)` - Build OAuth authorization URL
395
+ * - `exchangeCodeForTokens(code, codeVerifier, redirectUri)` - Exchange code for tokens
396
+ * - `refreshToken(auth)` - Refresh an expired access token
397
+ * - `isAuthValid(auth)` - Check if auth data is still valid
398
+ *
399
+ * ### Device Flow Authentication (RFC 8628)
400
+ * - `supportsDeviceFlow()` - Whether device flow is supported
401
+ * - `initiateDeviceFlow()` - Start device authorization
402
+ * - `pollDeviceFlowToken(deviceCode)` - Poll for token after user authorizes
403
+ */
404
+ declare abstract class ContentProvider {
405
+ /** Unique identifier for the provider (e.g., 'lessonschurch', 'aplay') */
406
+ abstract readonly id: string;
407
+ /** Display name of the provider */
408
+ abstract readonly name: string;
409
+ /** Provider logos for light and dark themes */
410
+ abstract readonly logos: ProviderLogos;
411
+ /** Provider configuration including API endpoints and OAuth settings */
412
+ abstract readonly config: ContentProviderConfig;
413
+ /**
414
+ * Browse the content hierarchy. If folder is null/undefined, returns root-level items.
415
+ * If folder is provided, returns items within that folder.
416
+ * @param folder - Optional folder to browse into (null/undefined for root)
417
+ * @param auth - Optional authentication data
418
+ * @returns Array of content items (folders and/or files)
419
+ */
420
+ abstract browse(folder?: ContentFolder | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
421
+ /**
422
+ * Get a structured plan with sections and presentations for a folder.
423
+ * @param folder - The folder to get presentations for (typically a venue or playlist)
424
+ * @param auth - Optional authentication data
425
+ * @returns Plan object with sections, presentations, and files, or null if not supported
426
+ */
427
+ abstract getPresentations(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
428
+ /**
429
+ * Get a flat list of media files (playlist) for a folder.
430
+ * Override in subclass if the provider supports playlists.
431
+ * @param _folder - The folder to get playlist for (typically a venue or playlist folder)
432
+ * @param _auth - Optional authentication data
433
+ * @param _resolution - Optional resolution hint for video quality
434
+ * @returns Array of ContentFile objects, or null if not supported
435
+ */
436
+ getPlaylist(_folder: ContentFolder, _auth?: ContentProviderAuthData | null, _resolution?: number): Promise<ContentFile[] | null>;
437
+ /**
438
+ * Get instruction/run sheet data for a folder.
439
+ * Override in subclass if the provider supports instructions.
440
+ * @param _folder - The folder to get instructions for
441
+ * @param _auth - Optional authentication data
442
+ * @returns Instructions object, or null if not supported
443
+ */
444
+ getInstructions(_folder: ContentFolder, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
445
+ /**
446
+ * Get expanded instruction data with actions for a folder.
447
+ * Override in subclass if the provider supports expanded instructions.
448
+ * @param _folder - The folder to get expanded instructions for
449
+ * @param _auth - Optional authentication data
450
+ * @returns Instructions object with expanded action data, or null if not supported
451
+ */
452
+ getExpandedInstructions(_folder: ContentFolder, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
453
+ /**
454
+ * Check if this provider requires authentication.
455
+ * @returns true if authentication is required
456
+ */
457
+ requiresAuth(): boolean;
458
+ /**
459
+ * Get the capabilities supported by this provider.
460
+ * Override in subclass to indicate supported features.
461
+ * @returns ProviderCapabilities object
462
+ */
463
+ getCapabilities(): ProviderCapabilities;
464
+ /**
465
+ * Check the license status for a specific media item.
466
+ * Override in subclass if the provider requires license validation.
467
+ * @param _mediaId - The media ID to check
468
+ * @param _auth - Optional authentication data
469
+ * @returns MediaLicenseResult object, or null if not supported
470
+ */
471
+ checkMediaLicense(_mediaId: string, _auth?: ContentProviderAuthData | null): Promise<MediaLicenseResult | null>;
472
+ /**
473
+ * Get the authentication types supported by this provider.
474
+ * @returns Array of supported AuthType values ('none', 'oauth_pkce', 'device_flow')
475
+ */
476
+ getAuthTypes(): AuthType[];
477
+ /**
478
+ * Check if the provided auth data is still valid (not expired).
479
+ * @param auth - Authentication data to validate
480
+ * @returns true if auth is valid and not expired
481
+ */
482
+ isAuthValid(auth: ContentProviderAuthData | null | undefined): boolean;
483
+ /**
484
+ * Check if a token is expired (with 5-minute buffer).
485
+ * @param auth - Authentication data to check
486
+ * @returns true if token is expired or will expire within 5 minutes
487
+ */
488
+ isTokenExpired(auth: ContentProviderAuthData): boolean;
489
+ /**
490
+ * Generate a random code verifier for PKCE.
491
+ * @returns A 64-character random string
492
+ */
493
+ generateCodeVerifier(): string;
494
+ /**
495
+ * Generate a code challenge from a code verifier using SHA-256.
496
+ * @param verifier - The code verifier string
497
+ * @returns Base64url-encoded SHA-256 hash of the verifier
498
+ */
499
+ generateCodeChallenge(verifier: string): Promise<string>;
500
+ /**
501
+ * Build the OAuth authorization URL for PKCE flow.
502
+ * @param codeVerifier - The code verifier (store this for token exchange)
503
+ * @param redirectUri - The redirect URI to return to after authorization
504
+ * @param state - Optional state parameter for CSRF protection (defaults to provider ID)
505
+ * @returns Object with authorization URL and challenge method
506
+ */
507
+ buildAuthUrl(codeVerifier: string, redirectUri: string, state?: string): Promise<{
508
+ url: string;
509
+ challengeMethod: string;
510
+ }>;
511
+ /**
512
+ * Exchange an authorization code for access and refresh tokens.
513
+ * @param code - The authorization code from the callback
514
+ * @param codeVerifier - The original code verifier used to generate the challenge
515
+ * @param redirectUri - The redirect URI (must match the one used in buildAuthUrl)
516
+ * @returns Authentication data, or null if exchange failed
517
+ */
518
+ exchangeCodeForTokens(code: string, codeVerifier: string, redirectUri: string): Promise<ContentProviderAuthData | null>;
519
+ /**
520
+ * Refresh an expired access token using the refresh token.
521
+ * @param auth - The current authentication data (must include refresh_token)
522
+ * @returns New authentication data, or null if refresh failed
523
+ */
524
+ refreshToken(auth: ContentProviderAuthData): Promise<ContentProviderAuthData | null>;
525
+ /**
526
+ * Check if this provider supports device flow authentication.
527
+ * @returns true if device flow is supported
528
+ */
529
+ supportsDeviceFlow(): boolean;
530
+ /**
531
+ * Initiate the device authorization flow (RFC 8628).
532
+ * @returns Device authorization response with user_code and verification_uri, or null if not supported
533
+ */
534
+ initiateDeviceFlow(): Promise<DeviceAuthorizationResponse | null>;
535
+ /**
536
+ * Poll for a token after user has authorized the device.
537
+ * @param deviceCode - The device_code from initiateDeviceFlow response
538
+ * @returns Auth data if successful, error object if pending/slow_down, or null if failed/expired
539
+ */
540
+ pollDeviceFlowToken(deviceCode: string): Promise<DeviceFlowPollResult>;
541
+ /**
542
+ * Calculate the delay between device flow poll attempts.
543
+ * @param baseInterval - Base interval in seconds (default: 5)
544
+ * @param slowDownCount - Number of slow_down responses received
545
+ * @returns Delay in milliseconds
546
+ */
547
+ calculatePollDelay(baseInterval?: number, slowDownCount?: number): number;
548
+ /**
549
+ * Create authorization headers for API requests.
550
+ * @param auth - Authentication data
551
+ * @returns Headers object with Authorization header, or null if no auth
552
+ */
553
+ protected createAuthHeaders(auth: ContentProviderAuthData | null | undefined): Record<string, string> | null;
554
+ /**
555
+ * Make an authenticated API request.
556
+ * @param path - API endpoint path (appended to config.apiBase)
557
+ * @param auth - Optional authentication data
558
+ * @param method - HTTP method (default: 'GET')
559
+ * @param body - Optional request body (for POST requests)
560
+ * @returns Parsed JSON response, or null if request failed
561
+ */
562
+ protected apiRequest<T>(path: string, auth?: ContentProviderAuthData | null, method?: 'GET' | 'POST', body?: unknown): Promise<T | null>;
563
+ /**
564
+ * Helper to create a ContentFolder object.
565
+ * @param id - Unique identifier
566
+ * @param title - Display title
567
+ * @param image - Optional image URL
568
+ * @param providerData - Optional provider-specific data
569
+ * @returns ContentFolder object
570
+ */
571
+ protected createFolder(id: string, title: string, image?: string, providerData?: Record<string, unknown>): ContentFolder;
572
+ /**
573
+ * Helper to create a ContentFile object with automatic media type detection.
574
+ * @param id - Unique identifier
575
+ * @param title - Display title
576
+ * @param url - Media URL
577
+ * @param options - Optional properties (mediaType, image, muxPlaybackId, providerData)
578
+ * @returns ContentFile object
579
+ */
580
+ protected createFile(id: string, title: string, url: string, options?: {
581
+ mediaType?: 'video' | 'image';
582
+ image?: string;
583
+ muxPlaybackId?: string;
584
+ providerData?: Record<string, unknown>;
585
+ }): ContentFile;
586
+ }
587
+
588
+ declare class APlayProvider extends ContentProvider {
589
+ readonly id = "aplay";
590
+ readonly name = "APlay";
591
+ readonly logos: ProviderLogos;
592
+ readonly config: ContentProviderConfig;
593
+ getCapabilities(): ProviderCapabilities;
594
+ browse(folder?: ContentFolder | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
595
+ private getProductFolders;
596
+ private getLibraryFolders;
597
+ private getMediaFiles;
598
+ getPresentations(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
599
+ /**
600
+ * Check the license status for a specific media item.
601
+ * Returns license information including pingback URL if licensed.
602
+ */
603
+ checkMediaLicense(mediaId: string, auth?: ContentProviderAuthData | null): Promise<MediaLicenseResult | null>;
604
+ }
605
+
606
+ declare class SignPresenterProvider extends ContentProvider {
607
+ readonly id = "signpresenter";
608
+ readonly name = "SignPresenter";
609
+ readonly logos: ProviderLogos;
610
+ readonly config: ContentProviderConfig;
611
+ getCapabilities(): ProviderCapabilities;
612
+ browse(folder?: ContentFolder | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
613
+ private getMessages;
614
+ getPresentations(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
615
+ }
616
+
617
+ declare class LessonsChurchProvider extends ContentProvider {
618
+ readonly id = "lessonschurch";
619
+ readonly name = "Lessons.church";
620
+ readonly logos: ProviderLogos;
621
+ readonly config: ContentProviderConfig;
622
+ requiresAuth(): boolean;
623
+ getCapabilities(): ProviderCapabilities;
624
+ getPlaylist(folder: ContentFolder, _auth?: ContentProviderAuthData | null, resolution?: number): Promise<ContentFile[] | null>;
625
+ protected apiRequest<T>(path: string): Promise<T | null>;
626
+ browse(folder?: ContentFolder | null, _auth?: ContentProviderAuthData | null, resolution?: number): Promise<ContentItem[]>;
627
+ private getPrograms;
628
+ private getStudies;
629
+ private getLessons;
630
+ private getVenues;
631
+ private getPlaylistFiles;
632
+ private getAddOnCategories;
633
+ private getAddOnsByCategory;
634
+ private convertAddOnToFile;
635
+ getPresentations(folder: ContentFolder, _auth?: ContentProviderAuthData | null, resolution?: number): Promise<Plan | null>;
636
+ getInstructions(folder: ContentFolder, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
637
+ getExpandedInstructions(folder: ContentFolder, _auth?: ContentProviderAuthData | null): Promise<Instructions | null>;
638
+ private getEmbedUrl;
639
+ private convertVenueToPlan;
640
+ }
641
+
642
+ declare class B1ChurchProvider extends ContentProvider {
643
+ readonly id = "b1church";
644
+ readonly name = "B1.Church";
645
+ readonly logos: ProviderLogos;
646
+ readonly config: ContentProviderConfig;
647
+ private appBase;
648
+ requiresAuth(): boolean;
649
+ getCapabilities(): ProviderCapabilities;
650
+ buildAuthUrl(_codeVerifier: string, redirectUri: string, state?: string): Promise<{
651
+ url: string;
652
+ challengeMethod: string;
653
+ }>;
654
+ exchangeCodeForTokensWithSecret(code: string, redirectUri: string, clientSecret: string): Promise<ContentProviderAuthData | null>;
655
+ refreshTokenWithSecret(authData: ContentProviderAuthData, clientSecret: string): Promise<ContentProviderAuthData | null>;
656
+ initiateDeviceFlow(): Promise<DeviceAuthorizationResponse | null>;
657
+ pollDeviceFlowToken(deviceCode: string): Promise<DeviceFlowPollResult>;
658
+ /**
659
+ * Browse content hierarchy:
660
+ * - Root: List of ministries (groups with "ministry" tag)
661
+ * - Ministry: List of plan types
662
+ * - PlanType: List of plans (leaf nodes)
663
+ *
664
+ * Plans are leaf nodes - use getPresentations(), getPlaylist(), getInstructions()
665
+ * to get plan content.
666
+ */
667
+ browse(folder?: ContentFolder | null, authData?: ContentProviderAuthData | null): Promise<ContentItem[]>;
668
+ getPresentations(folder: ContentFolder, authData?: ContentProviderAuthData | null): Promise<Plan | null>;
669
+ getInstructions(folder: ContentFolder, authData?: ContentProviderAuthData | null): Promise<Instructions | null>;
670
+ getPlaylist(folder: ContentFolder, authData?: ContentProviderAuthData | null): Promise<ContentFile[]>;
671
+ }
672
+
673
+ declare class PlanningCenterProvider extends ContentProvider {
674
+ readonly id = "planningcenter";
675
+ readonly name = "Planning Center";
676
+ readonly logos: ProviderLogos;
677
+ readonly config: ContentProviderConfig;
678
+ private readonly ONE_WEEK_MS;
679
+ requiresAuth(): boolean;
680
+ getCapabilities(): ProviderCapabilities;
681
+ browse(folder?: ContentFolder | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
682
+ private getPlans;
683
+ private getPlanItems;
684
+ getPresentations(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
685
+ private convertToPresentation;
686
+ private convertSongToPresentation;
687
+ private convertMediaToPresentation;
688
+ private formatDate;
689
+ }
690
+
691
+ declare class BibleProjectProvider extends ContentProvider {
692
+ readonly id = "bibleproject";
693
+ readonly name = "The Bible Project";
694
+ readonly logos: ProviderLogos;
695
+ readonly config: ContentProviderConfig;
696
+ private data;
697
+ requiresAuth(): boolean;
698
+ getCapabilities(): ProviderCapabilities;
699
+ browse(folder?: ContentFolder | null, _auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
700
+ getPresentations(_folder: ContentFolder, _auth?: ContentProviderAuthData | null): Promise<Plan | null>;
701
+ private getLessonFolders;
702
+ private slugify;
703
+ }
704
+
705
+ /**
706
+ * Get a provider by ID.
707
+ */
708
+ declare function getProvider(providerId: string): ContentProvider | null;
709
+ /**
710
+ * Get all registered providers.
711
+ */
712
+ declare function getAllProviders(): ContentProvider[];
713
+ /**
714
+ * Register a custom provider.
715
+ */
716
+ declare function registerProvider(provider: ContentProvider): void;
717
+ /**
718
+ * Get provider configuration by ID (for backward compatibility).
719
+ */
720
+ declare function getProviderConfig(providerId: string): ContentProviderConfig | null;
721
+ /**
722
+ * Get list of available providers with their info including logos and auth types.
723
+ * Includes both implemented providers and coming soon providers.
724
+ */
725
+ declare function getAvailableProviders(): ProviderInfo[];
726
+
727
+ /**
728
+ * @churchapps/content-provider-helper
729
+ * Helper classes for interacting with third party content providers
730
+ */
731
+ declare const VERSION = "0.0.1";
732
+
733
+ 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, type InstructionItem, type Instructions, LessonsChurchProvider, type MediaLicenseResult, type MediaLicenseStatus, type Plan, type PlanPresentation, type PlanSection, PlanningCenterProvider, type ProviderCapabilities, type ProviderInfo, type ProviderLogos, SignPresenterProvider, VERSION, type VenueActionInterface, type VenueActionsResponseInterface, type VenueSectionActionsInterface, detectMediaType, getAllProviders, getAvailableProviders, getProvider, getProviderConfig, isContentFile, isContentFolder, registerProvider };