@churchapps/content-providers 0.1.4 → 0.1.6

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
@@ -620,7 +620,7 @@ declare class HighVoltageKidsProvider implements IProvider {
620
620
  * / -> categories (Feature Films, Series, Collections)
621
621
  * /{category} -> list items in category
622
622
  * /{category}/{id} -> single video (feature film) or container children (series/collection)
623
- * /{category}/{containerId}/{videoId} -> single video within a container
623
+ * /{category}/{...containerIds}/{id} -> nested containers or single video (supports arbitrary depth)
624
624
  */
625
625
  declare class JesusFilmProvider implements IProvider {
626
626
  readonly id = "jesusfilm";
package/dist/index.d.ts CHANGED
@@ -620,7 +620,7 @@ declare class HighVoltageKidsProvider implements IProvider {
620
620
  * / -> categories (Feature Films, Series, Collections)
621
621
  * /{category} -> list items in category
622
622
  * /{category}/{id} -> single video (feature film) or container children (series/collection)
623
- * /{category}/{containerId}/{videoId} -> single video within a container
623
+ * /{category}/{...containerIds}/{id} -> nested containers or single video (supports arbitrary depth)
624
624
  */
625
625
  declare class JesusFilmProvider implements IProvider {
626
626
  readonly id = "jesusfilm";
package/dist/index.js CHANGED
@@ -486,13 +486,18 @@ var ApiHelper = class {
486
486
  const headers = { Accept: "application/json" };
487
487
  if (auth) headers["Authorization"] = `Bearer ${auth.access_token}`;
488
488
  if (body) headers["Content-Type"] = "application/json";
489
+ console.log(`[B1Church] apiRequest: ${method} ${url}`);
489
490
  const options = { method, headers, ...body ? { body: JSON.stringify(body) } : {} };
490
491
  const response = await fetch(url, options);
491
492
  if (!response.ok) {
493
+ console.warn(`[B1Church] apiRequest failed: ${method} ${url} \u2192 HTTP ${response.status} ${response.statusText}`);
492
494
  return null;
493
495
  }
494
- return await response.json();
495
- } catch {
496
+ const data = await response.json();
497
+ console.log(`[B1Church] apiRequest OK: ${method} ${url} \u2192 ${Array.isArray(data) ? data.length + " items" : typeof data}`);
498
+ return data;
499
+ } catch (err) {
500
+ console.error(`[B1Church] apiRequest error: ${method} ${config.apiBase}${path}`, err);
496
501
  return null;
497
502
  }
498
503
  }
@@ -1367,10 +1372,17 @@ async function authFetch(url, auth) {
1367
1372
  if (auth) {
1368
1373
  headers["Authorization"] = `Bearer ${auth.access_token}`;
1369
1374
  }
1375
+ console.log(`[B1Church] authFetch: ${url}`);
1370
1376
  const response = await fetch(url, { method: "GET", headers });
1371
- if (!response.ok) return null;
1372
- return await response.json();
1373
- } catch {
1377
+ if (!response.ok) {
1378
+ console.warn(`[B1Church] authFetch failed: ${url} \u2192 HTTP ${response.status} ${response.statusText}`);
1379
+ return null;
1380
+ }
1381
+ const data = await response.json();
1382
+ console.log(`[B1Church] authFetch OK: ${url} \u2192 ${Array.isArray(data) ? data.length + " items" : typeof data}`);
1383
+ return data;
1384
+ } catch (err) {
1385
+ console.error(`[B1Church] authFetch error: ${url}`, err);
1374
1386
  return null;
1375
1387
  }
1376
1388
  }
@@ -1389,20 +1401,34 @@ async function fetchPlans(planTypeId, auth) {
1389
1401
  async function fetchVenueFeed(venueId) {
1390
1402
  try {
1391
1403
  const url = `${LESSONS_API_BASE}/venues/public/feed/${venueId}`;
1404
+ console.log(`[B1Church] fetchVenueFeed: ${url}`);
1392
1405
  const response = await fetch(url, { method: "GET", headers: { Accept: "application/json" } });
1393
- if (!response.ok) return null;
1394
- return await response.json();
1395
- } catch {
1406
+ if (!response.ok) {
1407
+ console.warn(`[B1Church] fetchVenueFeed failed: HTTP ${response.status} ${response.statusText} for venueId=${venueId}`);
1408
+ return null;
1409
+ }
1410
+ const data = await response.json();
1411
+ console.log(`[B1Church] fetchVenueFeed OK: venueId=${venueId}, sections=${data?.sections?.length ?? "none"}`);
1412
+ return data;
1413
+ } catch (err) {
1414
+ console.error(`[B1Church] fetchVenueFeed error: venueId=${venueId}`, err);
1396
1415
  return null;
1397
1416
  }
1398
1417
  }
1399
1418
  async function fetchVenueActions(venueId) {
1400
1419
  try {
1401
1420
  const url = `${LESSONS_API_BASE}/venues/public/actions/${venueId}`;
1421
+ console.log(`[B1Church] fetchVenueActions: ${url}`);
1402
1422
  const response = await fetch(url, { method: "GET", headers: { Accept: "application/json" } });
1403
- if (!response.ok) return null;
1404
- return await response.json();
1405
- } catch {
1423
+ if (!response.ok) {
1424
+ console.warn(`[B1Church] fetchVenueActions failed: HTTP ${response.status} ${response.statusText} for venueId=${venueId}`);
1425
+ return null;
1426
+ }
1427
+ const data = await response.json();
1428
+ console.log(`[B1Church] fetchVenueActions OK: venueId=${venueId}`);
1429
+ return data;
1430
+ } catch (err) {
1431
+ console.error(`[B1Church] fetchVenueActions error: venueId=${venueId}`, err);
1406
1432
  return null;
1407
1433
  }
1408
1434
  }
@@ -1418,14 +1444,21 @@ async function fetchFromProviderProxy(method, ministryId, providerId, path, auth
1418
1444
  }
1419
1445
  const body = { ministryId, providerId, path };
1420
1446
  if (resolution !== void 0) body.resolution = resolution;
1447
+ console.log(`[B1Church] providerProxy: ${method} providerId=${providerId} path=${path}`);
1421
1448
  const response = await fetch(url, {
1422
1449
  method: "POST",
1423
1450
  headers,
1424
1451
  body: JSON.stringify(body)
1425
1452
  });
1426
- if (!response.ok) return null;
1427
- return await response.json();
1428
- } catch {
1453
+ if (!response.ok) {
1454
+ console.warn(`[B1Church] providerProxy failed: ${method} \u2192 HTTP ${response.status} ${response.statusText}`);
1455
+ return null;
1456
+ }
1457
+ const data = await response.json();
1458
+ console.log(`[B1Church] providerProxy OK: ${method} \u2192 ${Array.isArray(data) ? data.length + " items" : typeof data}`);
1459
+ return data;
1460
+ } catch (err) {
1461
+ console.error(`[B1Church] providerProxy error: ${method}`, err);
1429
1462
  return null;
1430
1463
  }
1431
1464
  }
@@ -1853,23 +1886,35 @@ var B1ChurchProvider = class {
1853
1886
  return null;
1854
1887
  }
1855
1888
  async getPlaylist(path, authData, resolution) {
1889
+ console.log(`[B1Church] getPlaylist called with path=${path}`);
1856
1890
  const { segments, depth } = parsePath(path);
1857
- if (depth < 4 || segments[0] !== "ministries") return null;
1891
+ if (depth < 4 || segments[0] !== "ministries") {
1892
+ console.warn(`[B1Church] getPlaylist: invalid path depth=${depth} segments=${JSON.stringify(segments)}`);
1893
+ return null;
1894
+ }
1858
1895
  const ministryId = segments[1];
1859
1896
  const planId = segments[3];
1860
1897
  const planTypeId = segments[2];
1898
+ console.log(`[B1Church] getPlaylist: ministryId=${ministryId}, planTypeId=${planTypeId}, planId=${planId}`);
1861
1899
  const plans = await fetchPlans(planTypeId, authData);
1900
+ console.log(`[B1Church] getPlaylist: fetchPlans returned ${plans.length} plans`);
1862
1901
  const planFolder = plans.find((p) => p.id === planId);
1863
- if (!planFolder) return null;
1902
+ if (!planFolder) {
1903
+ console.warn(`[B1Church] getPlaylist: plan ${planId} not found in ${plans.length} plans (ids: ${plans.map((p) => p.id).join(", ")})`);
1904
+ return null;
1905
+ }
1864
1906
  const churchId = planFolder.churchId;
1865
1907
  const venueId = planFolder.contentId;
1908
+ console.log(`[B1Church] getPlaylist: planFolder found \u2014 churchId=${churchId}, venueId=${venueId}, providerId=${planFolder.providerId}, providerPlanId=${planFolder.providerPlanId}`);
1866
1909
  if (!churchId) {
1867
1910
  console.warn("[B1Church getPlaylist] planFolder missing churchId:", planFolder.id);
1868
1911
  return null;
1869
1912
  }
1870
1913
  const pathFn = this.config.endpoints.planItems;
1871
1914
  const planItems = await this.apiRequest(pathFn(churchId, planId), authData);
1915
+ console.log(`[B1Church] getPlaylist: planItems=${planItems ? Array.isArray(planItems) ? planItems.length + " sections" : typeof planItems : "null"}`);
1872
1916
  if ((!planItems || planItems.length === 0) && planFolder.providerId && planFolder.providerPlanId) {
1917
+ console.log(`[B1Church] getPlaylist: no planItems, trying external provider ${planFolder.providerId} with path ${planFolder.providerPlanId}`);
1873
1918
  const externalFiles = await fetchFromProviderProxy(
1874
1919
  "getPlaylist",
1875
1920
  ministryId,
@@ -1878,17 +1923,30 @@ var B1ChurchProvider = class {
1878
1923
  authData,
1879
1924
  resolution
1880
1925
  );
1926
+ console.log(`[B1Church] getPlaylist: external provider returned ${externalFiles ? Array.isArray(externalFiles) ? externalFiles.length + " files" : typeof externalFiles : "null"}`);
1881
1927
  return externalFiles || null;
1882
1928
  }
1883
- if (!planItems || !Array.isArray(planItems)) return null;
1929
+ if (!planItems || !Array.isArray(planItems)) {
1930
+ console.warn(`[B1Church] getPlaylist: planItems is null/not-array and no external provider fallback. providerId=${planFolder.providerId}, providerPlanId=${planFolder.providerPlanId}`);
1931
+ return null;
1932
+ }
1933
+ console.log(`[B1Church] getPlaylist: processing ${planItems.length} sections, venueId=${venueId || "none"}`);
1884
1934
  const venueFeed = venueId ? await fetchVenueFeed(venueId) : null;
1935
+ if (venueId && !venueFeed) {
1936
+ console.warn(`[B1Church] getPlaylist: venueFeed is null for venueId=${venueId}`);
1937
+ }
1885
1938
  const files = [];
1886
1939
  for (const sectionItem of planItems) {
1940
+ console.log(`[B1Church] getPlaylist: section "${sectionItem.label || sectionItem.id}" has ${sectionItem.children?.length || 0} children`);
1887
1941
  for (const child of sectionItem.children || []) {
1888
1942
  const childItemType = child.itemType;
1943
+ console.log(`[B1Church] getPlaylist: child itemType=${childItemType}, relatedId=${child.relatedId}, providerId=${child.providerId}, providerPath=${child.providerPath}, link=${child.link ? "yes" : "no"}`);
1889
1944
  const isSectionType = childItemType === "section" || childItemType === "lessonSection" || childItemType === "providerSection";
1890
1945
  const canExpandLocally = isSectionType && venueFeed && child.relatedId;
1891
- if (isExternalProviderItem(child) && child.providerId && child.providerPath) {
1946
+ if (canExpandLocally) {
1947
+ const itemFiles = getFilesFromVenueFeed(venueFeed, childItemType, child.relatedId);
1948
+ files.push(...itemFiles);
1949
+ } else if (isExternalProviderItem(child) && child.providerId && child.providerPath) {
1892
1950
  const cacheKey = `${child.providerId}:${child.providerPath}`;
1893
1951
  if (child.providerContentPath) {
1894
1952
  let externalPlan = this.externalContentCache.plans.get(cacheKey);
@@ -1936,9 +1994,6 @@ var B1ChurchProvider = class {
1936
1994
  files.push(...externalFiles);
1937
1995
  }
1938
1996
  }
1939
- } else if (canExpandLocally) {
1940
- const itemFiles = getFilesFromVenueFeed(venueFeed, childItemType, child.relatedId);
1941
- files.push(...itemFiles);
1942
1997
  } else if ((childItemType === "providerFile" || childItemType === "providerPresentation") && child.link) {
1943
1998
  const file = getFileFromProviderFileItem(child);
1944
1999
  if (file) files.push(file);
@@ -1948,6 +2003,10 @@ var B1ChurchProvider = class {
1948
2003
  }
1949
2004
  }
1950
2005
  }
2006
+ console.log(`[B1Church] getPlaylist: total files collected = ${files.length}`);
2007
+ if (files.length === 0) {
2008
+ console.warn(`[B1Church] getPlaylist: returning null \u2014 no files found for path=${path}`);
2009
+ }
1951
2010
  return files.length > 0 ? files : null;
1952
2011
  }
1953
2012
  supportsDeviceFlow() {
@@ -12441,13 +12500,13 @@ var JesusFilmProvider = class {
12441
12500
  const { segments, depth } = parsePath(path);
12442
12501
  if (depth === 0) return this.getCategories();
12443
12502
  if (depth === 1) return this.getItemsInCategory(segments[0]);
12444
- if (depth === 2) {
12445
- const category = segments[0];
12446
- if (category === "feature-films") return this.getVideoFile(segments[1]);
12447
- return this.getContainerChildren(segments[1], category);
12448
- }
12449
- if (depth === 3) return this.getVideoFile(segments[2]);
12450
- return [];
12503
+ const category = segments[0];
12504
+ const lastId = segments[depth - 1];
12505
+ if (depth === 2 && category === "feature-films") return this.getVideoFile(lastId);
12506
+ const pathPrefix = "/" + segments.join("/");
12507
+ const children = await this.getContainerChildren(lastId, pathPrefix);
12508
+ if (children.length > 0) return children;
12509
+ return this.getVideoFile(lastId);
12451
12510
  }
12452
12511
  getCategories() {
12453
12512
  return [
@@ -12496,7 +12555,7 @@ var JesusFilmProvider = class {
12496
12555
  })
12497
12556
  ];
12498
12557
  }
12499
- async getContainerChildren(containerId, category) {
12558
+ async getContainerChildren(containerId, pathPrefix) {
12500
12559
  const linksData = await this.fetchApi(
12501
12560
  `/media-component-links/${containerId}`
12502
12561
  );
@@ -12510,9 +12569,9 @@ var JesusFilmProvider = class {
12510
12569
  return childrenData._embedded.mediaComponents.map((item) => createFolder(
12511
12570
  item.mediaComponentId,
12512
12571
  item.title,
12513
- `/${category}/${containerId}/${item.mediaComponentId}`,
12572
+ `${pathPrefix}/${item.mediaComponentId}`,
12514
12573
  item.imageUrls.mobileCinematicHigh || item.imageUrls.videoStill || item.imageUrls.thumbnail,
12515
- true
12574
+ item.containsCount === 0
12516
12575
  ));
12517
12576
  }
12518
12577
  extractMuxPlaybackId(url) {
@@ -12523,10 +12582,13 @@ var JesusFilmProvider = class {
12523
12582
  const { segments, depth } = parsePath(path);
12524
12583
  if (depth < 1) return null;
12525
12584
  const category = segments[0];
12526
- if (depth === 3) {
12527
- const items = await this.getVideoFile(segments[2]);
12528
- if (items.length === 0) return null;
12529
- return items.filter((item) => item.type === "file");
12585
+ if (depth >= 3) {
12586
+ const lastId = segments[depth - 1];
12587
+ const items = await this.getVideoFile(lastId);
12588
+ const files2 = items.filter((item) => item.type === "file");
12589
+ if (files2.length > 0) return files2;
12590
+ const containerFiles = await this.fetchContainerVideoFiles(lastId);
12591
+ return containerFiles.length > 0 ? containerFiles : null;
12530
12592
  }
12531
12593
  if (depth === 2) {
12532
12594
  if (category === "feature-films") {
@@ -12542,7 +12604,7 @@ var JesusFilmProvider = class {
12542
12604
  const data = await this.fetchApi(
12543
12605
  `/media-components?filter=master&subTypes=${subType}&languageIds=${LANGUAGE_ID}&limit=100`
12544
12606
  );
12545
- const contentComponents = data._embedded.mediaComponents.filter((item) => item.componentType === "content");
12607
+ const contentComponents = data._embedded.mediaComponents.filter((item) => item.containsCount === 0);
12546
12608
  const files = await this.fetchVideoFiles(contentComponents);
12547
12609
  return files.length > 0 ? files : null;
12548
12610
  }
@@ -12581,7 +12643,14 @@ var JesusFilmProvider = class {
12581
12643
  `/media-components?ids=${idsParam}&languageIds=${LANGUAGE_ID}`
12582
12644
  );
12583
12645
  if (!childrenData._embedded?.mediaComponents) return [];
12584
- return this.fetchVideoFiles(childrenData._embedded.mediaComponents);
12646
+ const contentItems = childrenData._embedded.mediaComponents.filter((c) => c.containsCount === 0);
12647
+ const containerItems = childrenData._embedded.mediaComponents.filter((c) => c.containsCount > 0);
12648
+ const files = await this.fetchVideoFiles(contentItems);
12649
+ for (const container of containerItems) {
12650
+ const nested = await this.fetchContainerVideoFiles(container.mediaComponentId);
12651
+ files.push(...nested);
12652
+ }
12653
+ return files;
12585
12654
  }
12586
12655
  async buildVideoInstructions(mediaComponentId, category) {
12587
12656
  const items = await this.getVideoFile(mediaComponentId);
@@ -12656,7 +12725,7 @@ var JesusFilmProvider = class {
12656
12725
  const actionItems = await Promise.all(
12657
12726
  data._embedded.mediaComponents.map(async (component) => {
12658
12727
  let downloadUrl;
12659
- if (component.componentType === "content") {
12728
+ if (component.containsCount === 0) {
12660
12729
  try {
12661
12730
  const variant = await this.fetchApi(
12662
12731
  `/media-components/${component.mediaComponentId}/languages/${LANGUAGE_ID}?platform=web`
@@ -12694,7 +12763,12 @@ var JesusFilmProvider = class {
12694
12763
  if (category === "feature-films") return this.buildVideoInstructions(segments[1], category);
12695
12764
  return this.buildContainerInstructions(segments[1], category);
12696
12765
  }
12697
- if (depth === 3) return this.buildVideoInstructions(segments[2], category);
12766
+ if (depth >= 3) {
12767
+ const lastId = segments[depth - 1];
12768
+ const videoResult = await this.buildVideoInstructions(lastId, category);
12769
+ if (videoResult) return videoResult;
12770
+ return this.buildContainerInstructions(lastId, category);
12771
+ }
12698
12772
  return null;
12699
12773
  }
12700
12774
  supportsDeviceFlow() {