@churchapps/content-providers 0.9.1 → 0.9.2

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.cjs CHANGED
@@ -242,7 +242,8 @@ var ApiHelper = class {
242
242
  const options = { method, headers, ...body ? { body: JSON.stringify(body) } : {} };
243
243
  const response = await fetch(url, options);
244
244
  if (!response.ok) {
245
- console.warn(`[${providerId}] apiRequest failed: ${method} ${url} \u2192 HTTP ${response.status} ${response.statusText}`);
245
+ const detail = await response.text().catch(() => "");
246
+ console.warn(`[${providerId}] apiRequest failed: ${method} ${url} \u2192 HTTP ${response.status} ${response.statusText}${detail ? ` ${detail.slice(0, 500)}` : ""}`);
246
247
  return null;
247
248
  }
248
249
  return await response.json();
@@ -303,16 +304,19 @@ var IMAGE_EXTENSIONS = [
303
304
  ".tiff",
304
305
  ".tif"
305
306
  ];
307
+ var AUDIO_EXTENSIONS = [".mp3", ".m4a", ".aac", ".wav", ".flac", ".oga"];
306
308
  function detectMediaType(url, explicitType) {
307
309
  if (explicitType === "video" || explicitType?.startsWith("video/")) return "video";
308
310
  if (explicitType === "image" || explicitType?.startsWith("image/")) return "image";
311
+ if (explicitType === "audio" || explicitType?.startsWith("audio/")) return "audio";
309
312
  const lower = url.toLowerCase();
310
313
  if (VIDEO_EXTENSIONS.some((p) => lower.includes(p)) || lower.includes("stream.mux.com")) return "video";
314
+ if (AUDIO_EXTENSIONS.some((p) => lower.includes(p))) return "audio";
311
315
  return "image";
312
316
  }
313
317
  function isMediaFile(filename) {
314
318
  const lower = filename.toLowerCase();
315
- return [...VIDEO_EXTENSIONS, ...IMAGE_EXTENSIONS].some((ext) => lower.endsWith(ext));
319
+ return [...VIDEO_EXTENSIONS, ...IMAGE_EXTENSIONS, ...AUDIO_EXTENSIONS].some((ext) => lower.endsWith(ext));
316
320
  }
317
321
  function createFolder(id, title, path, thumbnail, isLeaf) {
318
322
  return { type: "folder", id, title, path, thumbnail, isLeaf };
@@ -592,13 +596,45 @@ var SignPresenterProvider = class extends BaseProvider {
592
596
  isLeaf: true
593
597
  }));
594
598
  }
599
+ /** image and video/stream slides play directly; web slides only when the url is itself a media file. html/pdf/multi_zone and generic web pages aren't playable in ChurchApps. */
600
+ slideMediaType(slideType, url) {
601
+ if (slideType === "image") return "image";
602
+ if (slideType === "video" || slideType === "stream") return "video";
603
+ if (slideType === "web" && isMediaFile(url)) return detectMediaType(url);
604
+ return null;
605
+ }
606
+ /** The documented player contract; /content/playlists/:id/messages only covers templates 1 and 3 so most playlists come back empty there. */
595
607
  async getMessages(playlistId, auth) {
608
+ const response = await this.apiRequest(`/v2/playlists/${playlistId}`, auth);
609
+ const messages = this.extractList(response, "messages");
610
+ if (messages.length === 0) return this.getLegacyMessages(playlistId, auth);
611
+ const files = [];
612
+ messages.forEach((msg, messageIndex) => {
613
+ const slides = Array.isArray(msg.slides) ? msg.slides : [];
614
+ slides.forEach((slide, slideIndex) => {
615
+ const data = slide.data || {};
616
+ const url = typeof data.url === "string" ? data.url.trim() : "";
617
+ const mediaType = url ? this.slideMediaType(slide.type, url) : null;
618
+ if (!mediaType) return;
619
+ const file = createFile(`${playlistId}-${messageIndex}-${slideIndex}`, msg.name, url, {
620
+ mediaType,
621
+ thumbnail: msg.thumbnail,
622
+ seconds: slide.seconds,
623
+ loop: data.loop
624
+ });
625
+ file.downloadUrl = url;
626
+ files.push(file);
627
+ });
628
+ });
629
+ return files;
630
+ }
631
+ async getLegacyMessages(playlistId, auth) {
596
632
  const response = await this.apiRequest(`/content/playlists/${playlistId}/messages`, auth);
597
633
  if (!response) return [];
598
634
  const files = [];
599
635
  for (const msg of this.extractList(response, "messages")) {
600
- if (!msg.url) continue;
601
- const url = msg.url;
636
+ const url = [msg.url, msg.thumbnail, msg.image].map((v) => typeof v === "string" ? v.trim() : "").find(Boolean) || "";
637
+ if (!url) continue;
602
638
  const file = createFile(msg.id, msg.name, url, {
603
639
  mediaType: detectMediaType(url, msg.mediaType),
604
640
  thumbnail: msg.thumbnail || msg.image,
@@ -17812,7 +17848,9 @@ var GoogleDriveProvider = class extends BaseProvider {
17812
17848
  orderBy: "folder,name_natural",
17813
17849
  pageSize: "1000",
17814
17850
  supportsAllDrives: "true",
17815
- includeItemsFromAllDrives: "true"
17851
+ // corpora=user (not includeItemsFromAllDrives) — "root" is only meaningful in the user's own corpus, and
17852
+ // combining it with an all-drives search makes Google reject the query, which browse saw as an empty folder.
17853
+ corpora: "user"
17816
17854
  });
17817
17855
  if (pageToken) params.set("pageToken", pageToken);
17818
17856
  const response = await this.apiRequest(`/files?${params.toString()}`, auth);
@@ -17826,7 +17864,7 @@ var GoogleDriveProvider = class extends BaseProvider {
17826
17864
  async childItems(folderId, auth) {
17827
17865
  const entries = await this.listFiles(`'${folderId}' in parents and trashed=false`, auth);
17828
17866
  const folders = entries.filter((e) => e.mimeType === FOLDER_MIME);
17829
- const mediaFiles = entries.filter((e) => e.mimeType.startsWith("video/") || e.mimeType.startsWith("image/"));
17867
+ const mediaFiles = entries.filter((e) => e.mimeType !== FOLDER_MIME && !e.mimeType.startsWith("application/vnd.google-apps") && (["video/", "image/", "audio/"].some((prefix) => e.mimeType.startsWith(prefix)) || isMediaFile(e.name)));
17830
17868
  return { folders, mediaFiles };
17831
17869
  }
17832
17870
  /** One batched query marks which sibling folders have subfolders; null = unknown (isLeaf omitted). */
@@ -17983,7 +18021,7 @@ function getAvailableProviders(ids) {
17983
18021
  }
17984
18022
 
17985
18023
  // src/index.ts
17986
- var VERSION = true ? "0.9.1" : "dev";
18024
+ var VERSION = true ? "0.9.2" : "dev";
17987
18025
  // Annotate the CommonJS export names for ESM import in node:
17988
18026
  0 && (module.exports = {
17989
18027
  APlayProvider,