@churchapps/content-provider-helper 0.0.1 → 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
@@ -1,241 +1,99 @@
1
- /**
2
- * OAuth token data returned after successful authentication.
3
- */
4
1
  interface ContentProviderAuthData {
5
- /** The access token for API requests */
6
2
  access_token: string;
7
- /** The refresh token for obtaining new access tokens */
8
3
  refresh_token: string;
9
- /** Token type, typically "Bearer" */
10
4
  token_type: string;
11
- /** Unix timestamp (seconds) when the token was created */
12
5
  created_at: number;
13
- /** Token lifetime in seconds */
14
6
  expires_in: number;
15
- /** Space-separated list of granted scopes */
16
7
  scope: string;
17
8
  }
18
- /**
19
- * Configuration for a content provider's API and OAuth settings.
20
- */
21
9
  interface ContentProviderConfig {
22
- /** Unique identifier for the provider */
23
10
  id: string;
24
- /** Display name of the provider */
25
11
  name: string;
26
- /** Base URL for API requests */
27
12
  apiBase: string;
28
- /** Base URL for OAuth endpoints */
29
13
  oauthBase: string;
30
- /** OAuth client ID */
31
14
  clientId: string;
32
- /** OAuth scopes to request */
33
15
  scopes: string[];
34
- /** Whether the provider supports device flow authentication */
35
16
  supportsDeviceFlow?: boolean;
36
- /** Endpoint path for device authorization (relative to oauthBase) */
37
17
  deviceAuthEndpoint?: string;
38
- /** API endpoint paths - can be static strings or functions that generate paths */
39
18
  endpoints: Record<string, string | ((...args: string[]) => string)>;
40
19
  }
41
- /**
42
- * Response from the device authorization endpoint (RFC 8628).
43
- */
44
20
  interface DeviceAuthorizationResponse {
45
- /** Device verification code for polling */
46
21
  device_code: string;
47
- /** User code to display for manual entry */
48
22
  user_code: string;
49
- /** URL where user should authenticate */
50
23
  verification_uri: string;
51
- /** Complete URL with user code pre-filled */
52
24
  verification_uri_complete?: string;
53
- /** Seconds until the codes expire */
54
25
  expires_in: number;
55
- /** Minimum polling interval in seconds */
56
26
  interval?: number;
57
27
  }
58
- /**
59
- * Current state of a device flow authentication process.
60
- */
61
28
  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 */
29
+ status: "loading" | "awaiting_user" | "polling" | "success" | "error" | "expired";
73
30
  deviceAuth?: DeviceAuthorizationResponse;
74
- /** Error message if status is 'error' */
75
31
  error?: string;
76
- /** Number of poll attempts made */
77
32
  pollCount?: number;
78
33
  }
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
- */
34
+ type AuthType = "none" | "oauth_pkce" | "device_flow" | "form_login";
90
35
  interface ProviderLogos {
91
- /** Logo URL for light theme backgrounds */
92
36
  light: string;
93
- /** Logo URL for dark theme backgrounds */
94
37
  dark: string;
95
38
  }
96
- /**
97
- * Information about a content provider.
98
- */
99
39
  interface ProviderInfo {
100
- /** Unique identifier for the provider */
101
40
  id: string;
102
- /** Display name of the provider */
103
41
  name: string;
104
- /** Provider logos */
105
42
  logos: ProviderLogos;
106
- /** Whether the provider is fully implemented */
107
43
  implemented: boolean;
108
- /** Whether the provider requires authentication */
109
44
  requiresAuth: boolean;
110
- /** Supported authentication types */
111
45
  authTypes: AuthType[];
112
- /** Provider capabilities */
113
46
  capabilities: ProviderCapabilities;
114
47
  }
115
- /**
116
- * A folder in the content hierarchy. Can be navigated into to retrieve child items.
117
- */
118
48
  interface ContentFolder {
119
- /** Discriminator for type narrowing. Always `'folder'` */
120
- type: 'folder';
121
- /** Unique identifier for this folder */
49
+ type: "folder";
122
50
  id: string;
123
- /** Display title */
124
51
  title: string;
125
- /** Optional thumbnail/cover image URL */
126
52
  image?: string;
127
- /** Provider-specific data for navigation (e.g., level, parentId) */
53
+ isLeaf?: boolean;
54
+ path: string;
128
55
  providerData?: Record<string, unknown>;
129
56
  }
130
- /**
131
- * A playable media file (video or image).
132
- */
133
57
  interface ContentFile {
134
- /** Discriminator for type narrowing. Always `'file'` */
135
- type: 'file';
136
- /** Unique identifier for this file */
58
+ type: "file";
137
59
  id: string;
138
- /** Display title */
139
60
  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 */
61
+ mediaType: "video" | "image";
147
62
  image?: string;
148
- /** URL to the media file */
149
63
  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
64
  embedUrl?: string;
156
- /** Mux playback ID for Mux-hosted videos */
157
65
  muxPlaybackId?: string;
158
- /** Decryption key for encrypted media (provider-specific) */
159
66
  decryptionKey?: string;
160
- /** Provider-specific media ID for tracking/licensing */
161
67
  mediaId?: string;
162
- /** URL to ping after 30+ seconds of playback (licensing requirement) */
163
68
  pingbackUrl?: string;
164
- /** Provider-specific data (e.g., duration, loop settings) */
165
69
  providerData?: Record<string, unknown>;
166
70
  }
167
- /**
168
- * A content item - either a folder or a file.
169
- */
170
71
  type ContentItem = ContentFolder | ContentFile;
171
- /**
172
- * Type guard to check if a ContentItem is a ContentFolder.
173
- */
174
72
  declare function isContentFolder(item: ContentItem): item is ContentFolder;
175
- /**
176
- * Type guard to check if a ContentItem is a ContentFile.
177
- */
178
73
  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
74
  type DeviceFlowPollResult = ContentProviderAuthData | {
186
75
  error: string;
187
76
  shouldSlowDown?: boolean;
188
77
  } | null;
189
- /**
190
- * A presentation within a plan section (e.g., a song, video, or activity).
191
- */
192
78
  interface PlanPresentation {
193
- /** Unique identifier */
194
79
  id: string;
195
- /** Display name */
196
80
  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 */
81
+ actionType: "play" | "add-on" | "other";
205
82
  files: ContentFile[];
206
83
  }
207
- /**
208
- * A section within a plan containing multiple presentations.
209
- */
210
84
  interface PlanSection {
211
- /** Unique identifier */
212
85
  id: string;
213
- /** Section name (e.g., "Worship", "Message", "Closing") */
214
86
  name: string;
215
- /** Presentations within this section */
216
87
  presentations: PlanPresentation[];
217
88
  }
218
- /**
219
- * A complete plan/service with sections and presentations.
220
- * Returned by `getPresentations()`.
221
- */
222
89
  interface Plan {
223
- /** Unique identifier */
224
90
  id: string;
225
- /** Plan name */
226
91
  name: string;
227
- /** Optional description */
228
92
  description?: string;
229
- /** Optional cover image URL */
230
93
  image?: string;
231
- /** Ordered sections in the plan */
232
94
  sections: PlanSection[];
233
- /** Flat list of all files in the plan */
234
95
  allFiles: ContentFile[];
235
96
  }
236
- /**
237
- * A file within a venue feed.
238
- */
239
97
  interface FeedFileInterface {
240
98
  id?: string;
241
99
  name?: string;
@@ -244,26 +102,17 @@ interface FeedFileInterface {
244
102
  seconds?: number;
245
103
  fileType?: string;
246
104
  }
247
- /**
248
- * An action within a venue feed section.
249
- */
250
105
  interface FeedActionInterface {
251
106
  id?: string;
252
107
  actionType?: string;
253
108
  content?: string;
254
109
  files?: FeedFileInterface[];
255
110
  }
256
- /**
257
- * A section within a venue feed.
258
- */
259
111
  interface FeedSectionInterface {
260
112
  id?: string;
261
113
  name?: string;
262
114
  actions?: FeedActionInterface[];
263
115
  }
264
- /**
265
- * Complete venue feed data from Lessons.church API.
266
- */
267
116
  interface FeedVenueInterface {
268
117
  id?: string;
269
118
  lessonId?: string;
@@ -273,447 +122,513 @@ interface FeedVenueInterface {
273
122
  lessonImage?: string;
274
123
  sections?: FeedSectionInterface[];
275
124
  }
276
- /**
277
- * An item in the instruction hierarchy.
278
- */
279
125
  interface InstructionItem {
280
- /** Unique identifier */
281
126
  id?: string;
282
- /** Type of instruction item */
283
127
  itemType?: string;
284
- /** ID of related content */
285
128
  relatedId?: string;
286
- /** Display label */
287
129
  label?: string;
288
- /** Description or notes */
289
130
  description?: string;
290
- /** Duration in seconds */
291
131
  seconds?: number;
292
- /** Child instruction items */
293
132
  children?: InstructionItem[];
294
- /** URL for embedded content viewer */
295
133
  embedUrl?: string;
296
134
  }
297
- /**
298
- * Instructions/run sheet for a venue.
299
- * Returned by `getInstructions()` and `getExpandedInstructions()`.
300
- */
301
135
  interface Instructions {
302
- /** Name of the venue */
303
136
  venueName?: string;
304
- /** Hierarchical list of instruction items */
305
137
  items: InstructionItem[];
306
138
  }
307
- /**
308
- * An action within a venue section.
309
- */
310
139
  interface VenueActionInterface {
311
140
  id?: string;
312
141
  name?: string;
313
142
  actionType?: string;
314
143
  seconds?: number;
315
144
  }
316
- /**
317
- * A section with its actions.
318
- */
319
145
  interface VenueSectionActionsInterface {
320
146
  id?: string;
321
147
  name?: string;
322
148
  actions?: VenueActionInterface[];
323
149
  }
324
- /**
325
- * Response containing venue actions organized by section.
326
- */
327
150
  interface VenueActionsResponseInterface {
328
151
  venueName?: string;
329
152
  sections?: VenueSectionActionsInterface[];
330
153
  }
331
- /**
332
- * Capabilities supported by a content provider.
333
- */
334
154
  interface ProviderCapabilities {
335
- /** Whether the provider supports browsing content hierarchy */
336
155
  browse: boolean;
337
- /** Whether `getPresentations()` returns structured plan data */
338
156
  presentations: boolean;
339
- /** Whether `getPlaylist()` returns a flat list of media files */
340
157
  playlist: boolean;
341
- /** Whether `getInstructions()` returns instruction data */
342
158
  instructions: boolean;
343
- /** Whether `getExpandedInstructions()` returns expanded instruction data */
344
159
  expandedInstructions: boolean;
345
- /** Whether `checkMediaLicense()` returns license information */
346
160
  mediaLicensing: boolean;
347
161
  }
348
- /**
349
- * License status for media content.
350
- */
351
- type MediaLicenseStatus = 'valid' | 'expired' | 'not_licensed' | 'unknown';
352
- /**
353
- * Response from `checkMediaLicense()`.
354
- */
162
+ type MediaLicenseStatus = "valid" | "expired" | "not_licensed" | "unknown";
355
163
  interface MediaLicenseResult {
356
- /** The media ID that was checked */
357
164
  mediaId: string;
358
- /** Current license status */
359
165
  status: MediaLicenseStatus;
360
- /** Human-readable message about the license */
361
166
  message?: string;
362
- /** When the license expires (ISO 8601 string or Unix timestamp) */
363
167
  expiresAt?: string | number;
364
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
+ }
225
+
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;
365
234
 
366
235
  /**
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'
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
371
255
  */
372
- declare function detectMediaType(url: string, explicitType?: string): 'video' | 'image';
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;
270
+
271
+ declare function presentationsToPlaylist(plan: Plan): ContentFile[];
272
+ declare function presentationsToInstructions(plan: Plan): Instructions;
273
+ declare function presentationsToExpandedInstructions(plan: Plan): Instructions;
274
+ declare function instructionsToPlaylist(instructions: Instructions): ContentFile[];
275
+ declare const expandedInstructionsToPlaylist: typeof instructionsToPlaylist;
276
+ declare function instructionsToPresentations(instructions: Instructions, planId?: string): Plan;
277
+ declare const expandedInstructionsToPresentations: typeof instructionsToPresentations;
278
+ declare function collapseInstructions(instructions: Instructions, maxDepth?: number): Instructions;
279
+ declare function playlistToPresentations(files: ContentFile[], planName?: string, sectionName?: string): Plan;
280
+ declare function playlistToInstructions(files: ContentFile[], venueName?: string): Instructions;
281
+ declare const playlistToExpandedInstructions: typeof playlistToInstructions;
282
+
283
+ declare const FormatConverters_collapseInstructions: typeof collapseInstructions;
284
+ declare const FormatConverters_expandedInstructionsToPlaylist: typeof expandedInstructionsToPlaylist;
285
+ declare const FormatConverters_expandedInstructionsToPresentations: typeof expandedInstructionsToPresentations;
286
+ declare const FormatConverters_instructionsToPlaylist: typeof instructionsToPlaylist;
287
+ declare const FormatConverters_instructionsToPresentations: typeof instructionsToPresentations;
288
+ declare const FormatConverters_playlistToExpandedInstructions: typeof playlistToExpandedInstructions;
289
+ declare const FormatConverters_playlistToInstructions: typeof playlistToInstructions;
290
+ declare const FormatConverters_playlistToPresentations: typeof playlistToPresentations;
291
+ declare const FormatConverters_presentationsToExpandedInstructions: typeof presentationsToExpandedInstructions;
292
+ declare const FormatConverters_presentationsToInstructions: typeof presentationsToInstructions;
293
+ declare const FormatConverters_presentationsToPlaylist: typeof presentationsToPlaylist;
294
+ declare namespace FormatConverters {
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 };
296
+ }
297
+
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
+ }
373
362
 
374
363
  /**
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
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.
403
367
  */
404
- declare abstract class ContentProvider {
405
- /** Unique identifier for the provider (e.g., 'lessonschurch', 'aplay') */
368
+ declare abstract class ContentProvider implements IContentProvider, IAuthProvider {
406
369
  abstract readonly id: string;
407
- /** Display name of the provider */
408
370
  abstract readonly name: string;
409
- /** Provider logos for light and dark themes */
410
371
  abstract readonly logos: ProviderLogos;
411
- /** Provider configuration including API endpoints and OAuth settings */
412
372
  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
- */
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>;
457
382
  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
383
  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
384
  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
385
  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
386
  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
387
  isTokenExpired(auth: ContentProviderAuthData): boolean;
489
- /**
490
- * Generate a random code verifier for PKCE.
491
- * @returns A 64-character random string
492
- */
388
+ refreshToken(auth: ContentProviderAuthData): Promise<ContentProviderAuthData | null>;
493
389
  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
390
  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
391
  buildAuthUrl(codeVerifier: string, redirectUri: string, state?: string): Promise<{
508
392
  url: string;
509
393
  challengeMethod: string;
510
394
  }>;
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
395
  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
396
  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
397
  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
398
  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
399
  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
400
  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
- */
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;
580
403
  protected createFile(id: string, title: string, url: string, options?: {
581
- mediaType?: 'video' | 'image';
404
+ mediaType?: "video" | "image";
582
405
  image?: string;
583
406
  muxPlaybackId?: string;
584
407
  providerData?: Record<string, unknown>;
585
408
  }): ContentFile;
586
409
  }
587
410
 
588
- 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;
589
424
  readonly id = "aplay";
590
425
  readonly name = "APlay";
591
426
  readonly logos: ProviderLogos;
592
427
  readonly config: ContentProviderConfig;
593
- getCapabilities(): ProviderCapabilities;
594
- browse(folder?: ContentFolder | null, auth?: ContentProviderAuthData | null): Promise<ContentItem[]>;
595
- 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;
596
434
  private getLibraryFolders;
597
435
  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
- */
436
+ getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
603
437
  checkMediaLicense(mediaId: string, auth?: ContentProviderAuthData | null): Promise<MediaLicenseResult | null>;
604
438
  }
605
439
 
606
- 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;
607
450
  readonly id = "signpresenter";
608
451
  readonly name = "SignPresenter";
609
452
  readonly logos: ProviderLogos;
610
453
  readonly config: ContentProviderConfig;
611
- getCapabilities(): ProviderCapabilities;
612
- 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;
613
459
  private getMessages;
614
- getPresentations(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
460
+ getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
615
461
  }
616
462
 
617
- 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 {
618
477
  readonly id = "lessonschurch";
619
478
  readonly name = "Lessons.church";
620
479
  readonly logos: ProviderLogos;
621
480
  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[]>;
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;
627
488
  private getPrograms;
628
489
  private getStudies;
629
490
  private getLessons;
630
491
  private getVenues;
631
492
  private getPlaylistFiles;
493
+ private browseAddOns;
632
494
  private getAddOnCategories;
633
495
  private getAddOnsByCategory;
634
496
  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>;
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;
638
501
  private getEmbedUrl;
639
502
  private convertVenueToPlan;
640
503
  }
641
504
 
642
- declare class B1ChurchProvider extends ContentProvider {
505
+ declare class B1ChurchProvider implements IProvider {
506
+ private readonly apiHelper;
507
+ private apiRequest;
643
508
  readonly id = "b1church";
644
509
  readonly name = "B1.Church";
645
510
  readonly logos: ProviderLogos;
646
511
  readonly config: ContentProviderConfig;
647
512
  private appBase;
648
- requiresAuth(): boolean;
649
- getCapabilities(): ProviderCapabilities;
650
- 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<{
651
517
  url: string;
652
518
  challengeMethod: string;
653
519
  }>;
520
+ exchangeCodeForTokensWithPKCE(code: string, redirectUri: string, codeVerifier: string): Promise<ContentProviderAuthData | null>;
654
521
  exchangeCodeForTokensWithSecret(code: string, redirectUri: string, clientSecret: string): Promise<ContentProviderAuthData | null>;
655
522
  refreshTokenWithSecret(authData: ContentProviderAuthData, clientSecret: string): Promise<ContentProviderAuthData | null>;
656
523
  initiateDeviceFlow(): Promise<DeviceAuthorizationResponse | null>;
657
524
  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[]>;
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>;
671
531
  }
672
532
 
673
- 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;
674
544
  readonly id = "planningcenter";
675
545
  readonly name = "Planning Center";
676
546
  readonly logos: ProviderLogos;
677
547
  readonly config: ContentProviderConfig;
678
548
  private readonly ONE_WEEK_MS;
679
- requiresAuth(): boolean;
680
- getCapabilities(): ProviderCapabilities;
681
- 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;
682
554
  private getPlans;
683
555
  private getPlanItems;
684
- getPresentations(folder: ContentFolder, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
556
+ getPresentations(path: string, auth?: ContentProviderAuthData | null): Promise<Plan | null>;
685
557
  private convertToPresentation;
686
558
  private convertSongToPresentation;
687
559
  private convertMediaToPresentation;
688
560
  private formatDate;
689
561
  }
690
562
 
691
- 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 {
692
572
  readonly id = "bibleproject";
693
573
  readonly name = "The Bible Project";
694
574
  readonly logos: ProviderLogos;
695
575
  readonly config: ContentProviderConfig;
696
576
  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>;
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;
701
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>;
702
615
  private slugify;
616
+ private groupFilesIntoActions;
617
+ private getBaseName;
703
618
  }
704
619
 
705
620
  /**
706
621
  * Get a provider by ID.
707
622
  */
708
- declare function getProvider(providerId: string): ContentProvider | null;
623
+ declare function getProvider(providerId: string): IProvider | null;
709
624
  /**
710
625
  * Get all registered providers.
711
626
  */
712
- declare function getAllProviders(): ContentProvider[];
627
+ declare function getAllProviders(): IProvider[];
713
628
  /**
714
629
  * Register a custom provider.
715
630
  */
716
- declare function registerProvider(provider: ContentProvider): void;
631
+ declare function registerProvider(provider: IProvider): void;
717
632
  /**
718
633
  * Get provider configuration by ID (for backward compatibility).
719
634
  */
@@ -721,8 +636,9 @@ declare function getProviderConfig(providerId: string): ContentProviderConfig |
721
636
  /**
722
637
  * Get list of available providers with their info including logos and auth types.
723
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.
724
640
  */
725
- declare function getAvailableProviders(): ProviderInfo[];
641
+ declare function getAvailableProviders(ids?: string[]): ProviderInfo[];
726
642
 
727
643
  /**
728
644
  * @churchapps/content-provider-helper
@@ -730,4 +646,4 @@ declare function getAvailableProviders(): ProviderInfo[];
730
646
  */
731
647
  declare const VERSION = "0.0.1";
732
648
 
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 };
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 };