@churchapps/content-providers 0.8.0 → 0.9.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.
- package/dist/index.cjs +145 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +145 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17703,6 +17703,149 @@ var GoCurriculumProvider = class extends BaseProvider {
|
|
|
17703
17703
|
}
|
|
17704
17704
|
};
|
|
17705
17705
|
|
|
17706
|
+
// src/providers/googledrive/GoogleDriveProvider.ts
|
|
17707
|
+
var FOLDER_MIME = "application/vnd.google-apps.folder";
|
|
17708
|
+
var MAX_PAGINATION_CALLS2 = 10;
|
|
17709
|
+
var LEAF_CHECK_MAX_FOLDERS = 50;
|
|
17710
|
+
var GoogleDriveProvider = class extends BaseProvider {
|
|
17711
|
+
constructor() {
|
|
17712
|
+
super(...arguments);
|
|
17713
|
+
this.oauthHelper = new OAuthHelper();
|
|
17714
|
+
this.id = "googledrive";
|
|
17715
|
+
this.name = "Google Drive";
|
|
17716
|
+
this.logos = {
|
|
17717
|
+
light: "https://ssl.gstatic.com/images/branding/product/2x/drive_2020q4_48dp.png",
|
|
17718
|
+
dark: "https://ssl.gstatic.com/images/branding/product/2x/drive_2020q4_48dp.png"
|
|
17719
|
+
};
|
|
17720
|
+
this.config = {
|
|
17721
|
+
id: "googledrive",
|
|
17722
|
+
name: "Google Drive",
|
|
17723
|
+
apiBase: "https://www.googleapis.com/drive/v3",
|
|
17724
|
+
// Token endpoint host; the user-facing consent screen is accounts.google.com (see buildAuthUrlFromChallenge)
|
|
17725
|
+
oauthBase: "https://oauth2.googleapis.com",
|
|
17726
|
+
clientId: "214868247432-hao323qmsfu96h345udaifs8f1nc7i71.apps.googleusercontent.com",
|
|
17727
|
+
// Google requires client_secret even for PKCE public clients; injected server-side via setProviderSecret("googledrive", ...)
|
|
17728
|
+
get clientSecret() {
|
|
17729
|
+
return getProviderSecret("googledrive");
|
|
17730
|
+
},
|
|
17731
|
+
scopes: ["https://www.googleapis.com/auth/drive.readonly"]
|
|
17732
|
+
};
|
|
17733
|
+
this.requiresAuth = true;
|
|
17734
|
+
this.authTypes = ["oauth_pkce"];
|
|
17735
|
+
this.capabilities = {
|
|
17736
|
+
browse: true,
|
|
17737
|
+
playlist: true,
|
|
17738
|
+
instructions: true,
|
|
17739
|
+
mediaLicensing: false
|
|
17740
|
+
};
|
|
17741
|
+
}
|
|
17742
|
+
// Paths are Drive folder ids ("/<id>"), not names; root is "" or "/"
|
|
17743
|
+
folderIdFromPath(path) {
|
|
17744
|
+
const trimmed = (path || "").replace(/^\//, "");
|
|
17745
|
+
return trimmed || "root";
|
|
17746
|
+
}
|
|
17747
|
+
isValidId(id) {
|
|
17748
|
+
return /^[\w-]+$/.test(id);
|
|
17749
|
+
}
|
|
17750
|
+
async listFiles(query, auth) {
|
|
17751
|
+
if (!auth) {
|
|
17752
|
+
console.error("[GoogleDrive] No auth token provided");
|
|
17753
|
+
return [];
|
|
17754
|
+
}
|
|
17755
|
+
const all = [];
|
|
17756
|
+
let pageToken;
|
|
17757
|
+
for (let i = 0; i < MAX_PAGINATION_CALLS2; i++) {
|
|
17758
|
+
const params = new URLSearchParams({
|
|
17759
|
+
q: query,
|
|
17760
|
+
fields: "nextPageToken,files(id,name,mimeType,thumbnailLink,webContentLink,parents)",
|
|
17761
|
+
orderBy: "folder,name_natural",
|
|
17762
|
+
pageSize: "1000",
|
|
17763
|
+
supportsAllDrives: "true",
|
|
17764
|
+
includeItemsFromAllDrives: "true"
|
|
17765
|
+
});
|
|
17766
|
+
if (pageToken) params.set("pageToken", pageToken);
|
|
17767
|
+
const response = await this.apiRequest(`/files?${params.toString()}`, auth);
|
|
17768
|
+
if (!response) break;
|
|
17769
|
+
all.push(...response.files || []);
|
|
17770
|
+
pageToken = response.nextPageToken;
|
|
17771
|
+
if (!pageToken) break;
|
|
17772
|
+
}
|
|
17773
|
+
return all;
|
|
17774
|
+
}
|
|
17775
|
+
async childItems(folderId, auth) {
|
|
17776
|
+
const entries = await this.listFiles(`'${folderId}' in parents and trashed=false`, auth);
|
|
17777
|
+
const folders = entries.filter((e) => e.mimeType === FOLDER_MIME);
|
|
17778
|
+
const mediaFiles = entries.filter((e) => e.mimeType.startsWith("video/") || e.mimeType.startsWith("image/"));
|
|
17779
|
+
return { folders, mediaFiles };
|
|
17780
|
+
}
|
|
17781
|
+
/** One batched query marks which sibling folders have subfolders; null = unknown (isLeaf omitted). */
|
|
17782
|
+
async leafIds(folders, auth) {
|
|
17783
|
+
if (folders.length === 0 || folders.length > LEAF_CHECK_MAX_FOLDERS) return null;
|
|
17784
|
+
const parentClause = folders.map((f) => `'${f.id}' in parents`).join(" or ");
|
|
17785
|
+
const subfolders = await this.listFiles(`mimeType='${FOLDER_MIME}' and trashed=false and (${parentClause})`, auth);
|
|
17786
|
+
const parentsWithSubfolders = new Set(subfolders.flatMap((s) => s.parents || []));
|
|
17787
|
+
return new Set(folders.filter((f) => !parentsWithSubfolders.has(f.id)).map((f) => f.id));
|
|
17788
|
+
}
|
|
17789
|
+
toContentFile(file) {
|
|
17790
|
+
const url = file.webContentLink || `https://drive.google.com/uc?id=${file.id}&export=download`;
|
|
17791
|
+
return {
|
|
17792
|
+
type: "file",
|
|
17793
|
+
id: file.id,
|
|
17794
|
+
title: file.name,
|
|
17795
|
+
mediaType: detectMediaType(file.name, file.mimeType),
|
|
17796
|
+
thumbnail: file.thumbnailLink,
|
|
17797
|
+
url,
|
|
17798
|
+
downloadUrl: url
|
|
17799
|
+
};
|
|
17800
|
+
}
|
|
17801
|
+
async browse(path, auth) {
|
|
17802
|
+
const folderId = this.folderIdFromPath(path);
|
|
17803
|
+
if (!this.isValidId(folderId)) return [];
|
|
17804
|
+
const { folders, mediaFiles } = await this.childItems(folderId, auth);
|
|
17805
|
+
const leaves = await this.leafIds(folders, auth);
|
|
17806
|
+
const folderItems = folders.map((f) => ({ type: "folder", id: f.id, title: f.name, path: `/${f.id}`, isLeaf: leaves ? leaves.has(f.id) : void 0 }));
|
|
17807
|
+
return [...folderItems, ...mediaFiles.map((f) => this.toContentFile(f))];
|
|
17808
|
+
}
|
|
17809
|
+
async getPlaylist(path, auth, _resolution) {
|
|
17810
|
+
const folderId = this.folderIdFromPath(path);
|
|
17811
|
+
if (folderId === "root" || !this.isValidId(folderId)) return null;
|
|
17812
|
+
const { mediaFiles } = await this.childItems(folderId, auth);
|
|
17813
|
+
return mediaFiles.length > 0 ? mediaFiles.map((f) => this.toContentFile(f)) : null;
|
|
17814
|
+
}
|
|
17815
|
+
async getInstructions(path, auth) {
|
|
17816
|
+
const files = await this.getPlaylist(path, auth);
|
|
17817
|
+
if (!files) return null;
|
|
17818
|
+
const meta = await this.apiRequest(`/files/${this.folderIdFromPath(path)}?fields=name&supportsAllDrives=true`, auth);
|
|
17819
|
+
const folderName = meta?.name || "Google Drive";
|
|
17820
|
+
return filesToInstructions(folderName, files, { id: "googledrive-section", label: folderName });
|
|
17821
|
+
}
|
|
17822
|
+
generateCodeVerifier() {
|
|
17823
|
+
return this.oauthHelper.generateCodeVerifier();
|
|
17824
|
+
}
|
|
17825
|
+
async buildAuthUrl(codeVerifier, redirectUri, state) {
|
|
17826
|
+
const codeChallenge = await this.oauthHelper.generateCodeChallenge(codeVerifier);
|
|
17827
|
+
return { url: this.buildAuthUrlFromChallenge(codeChallenge, redirectUri, state || this.id), challengeMethod: "S256" };
|
|
17828
|
+
}
|
|
17829
|
+
buildAuthUrlFromChallenge(codeChallenge, redirectUri, state) {
|
|
17830
|
+
const params = new URLSearchParams({
|
|
17831
|
+
response_type: "code",
|
|
17832
|
+
client_id: this.config.clientId,
|
|
17833
|
+
redirect_uri: redirectUri,
|
|
17834
|
+
scope: this.config.scopes.join(" "),
|
|
17835
|
+
code_challenge: codeChallenge,
|
|
17836
|
+
code_challenge_method: "S256",
|
|
17837
|
+
// offline + consent so Google issues a refresh_token on every connect
|
|
17838
|
+
access_type: "offline",
|
|
17839
|
+
prompt: "consent",
|
|
17840
|
+
state
|
|
17841
|
+
});
|
|
17842
|
+
return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
|
|
17843
|
+
}
|
|
17844
|
+
async exchangeCodeForTokens(code, codeVerifier, redirectUri) {
|
|
17845
|
+
return this.oauthHelper.exchangeCodeForTokens(this.config, this.id, code, codeVerifier, redirectUri);
|
|
17846
|
+
}
|
|
17847
|
+
};
|
|
17848
|
+
|
|
17706
17849
|
// src/providers/index.ts
|
|
17707
17850
|
var unimplementedProviders = [
|
|
17708
17851
|
{
|
|
@@ -17744,6 +17887,7 @@ function initializeProviders() {
|
|
|
17744
17887
|
new B1ChurchProvider(),
|
|
17745
17888
|
new DropboxProvider(),
|
|
17746
17889
|
new GoCurriculumProvider(),
|
|
17890
|
+
new GoogleDriveProvider(),
|
|
17747
17891
|
new BibleProjectProvider(),
|
|
17748
17892
|
new HighVoltageKidsProvider(),
|
|
17749
17893
|
new JesusFilmProvider(),
|
|
@@ -17788,7 +17932,7 @@ function getAvailableProviders(ids) {
|
|
|
17788
17932
|
}
|
|
17789
17933
|
|
|
17790
17934
|
// src/index.ts
|
|
17791
|
-
var VERSION = true ? "0.
|
|
17935
|
+
var VERSION = true ? "0.9.1" : "dev";
|
|
17792
17936
|
export {
|
|
17793
17937
|
APlayProvider,
|
|
17794
17938
|
B1ChurchProvider,
|