@churchapps/content-providers 0.9.0 → 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 +46 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +46 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
601
|
-
|
|
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,
|
|
@@ -17774,8 +17810,7 @@ var GoogleDriveProvider = class extends BaseProvider {
|
|
|
17774
17810
|
apiBase: "https://www.googleapis.com/drive/v3",
|
|
17775
17811
|
// Token endpoint host; the user-facing consent screen is accounts.google.com (see buildAuthUrlFromChallenge)
|
|
17776
17812
|
oauthBase: "https://oauth2.googleapis.com",
|
|
17777
|
-
|
|
17778
|
-
clientId: "",
|
|
17813
|
+
clientId: "214868247432-hao323qmsfu96h345udaifs8f1nc7i71.apps.googleusercontent.com",
|
|
17779
17814
|
// Google requires client_secret even for PKCE public clients; injected server-side via setProviderSecret("googledrive", ...)
|
|
17780
17815
|
get clientSecret() {
|
|
17781
17816
|
return getProviderSecret("googledrive");
|
|
@@ -17813,7 +17848,9 @@ var GoogleDriveProvider = class extends BaseProvider {
|
|
|
17813
17848
|
orderBy: "folder,name_natural",
|
|
17814
17849
|
pageSize: "1000",
|
|
17815
17850
|
supportsAllDrives: "true",
|
|
17816
|
-
includeItemsFromAllDrives
|
|
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"
|
|
17817
17854
|
});
|
|
17818
17855
|
if (pageToken) params.set("pageToken", pageToken);
|
|
17819
17856
|
const response = await this.apiRequest(`/files?${params.toString()}`, auth);
|
|
@@ -17827,7 +17864,7 @@ var GoogleDriveProvider = class extends BaseProvider {
|
|
|
17827
17864
|
async childItems(folderId, auth) {
|
|
17828
17865
|
const entries = await this.listFiles(`'${folderId}' in parents and trashed=false`, auth);
|
|
17829
17866
|
const folders = entries.filter((e) => e.mimeType === FOLDER_MIME);
|
|
17830
|
-
const mediaFiles = entries.filter((e) => e.mimeType.startsWith("video/")
|
|
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)));
|
|
17831
17868
|
return { folders, mediaFiles };
|
|
17832
17869
|
}
|
|
17833
17870
|
/** One batched query marks which sibling folders have subfolders; null = unknown (isLeaf omitted). */
|
|
@@ -17984,7 +18021,7 @@ function getAvailableProviders(ids) {
|
|
|
17984
18021
|
}
|
|
17985
18022
|
|
|
17986
18023
|
// src/index.ts
|
|
17987
|
-
var VERSION = true ? "0.9.
|
|
18024
|
+
var VERSION = true ? "0.9.2" : "dev";
|
|
17988
18025
|
// Annotate the CommonJS export names for ESM import in node:
|
|
17989
18026
|
0 && (module.exports = {
|
|
17990
18027
|
APlayProvider,
|