This commit is contained in:
2025-11-04 10:56:30 +01:00
parent a2e0819524
commit c7472d833a
8 changed files with 60 additions and 62 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ npm-debug.*
web-build/ web-build/
ios/ ios/
android/ android/
youtube.md
# macOS # macOS
.DS_Store .DS_Store
-6
View File
@@ -1,6 +0,0 @@
YOUTUBE_CLIENT_ID=your-youtube-client-id
YOUTUBE_CLIENT_SECRET=your-youtube-client-secret
YOUTUBE_REFRESH_TOKEN=your-youtube-refresh-token
# YOUTUBE_REDIRECT_URI=http://localhost:8081
# YOUTUBE_PRIVACY_STATUS=unlisted
# YOUTUBE_CATEGORY_ID=10
-2
View File
@@ -1,5 +1,3 @@
require("dotenv").config();
const admin = require("firebase-admin"); const admin = require("firebase-admin");
// Use default credentials/environment provided by Cloud Functions. // Use default credentials/environment provided by Cloud Functions.
-12
View File
@@ -11,7 +11,6 @@
"@google-cloud/firestore": "^7.11.6", "@google-cloud/firestore": "^7.11.6",
"@google-cloud/storage": "^7.17.1", "@google-cloud/storage": "^7.17.1",
"axios": "^1.11.0", "axios": "^1.11.0",
"dotenv": "^17.2.3",
"expo-server-sdk": "^4.0.0", "expo-server-sdk": "^4.0.0",
"ffmpeg-static": "^5.2.0", "ffmpeg-static": "^5.2.0",
"firebase-admin": "^12.6.0", "firebase-admin": "^12.6.0",
@@ -3785,17 +3784,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/dotenv": {
"version": "17.2.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
"integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/dotprompt": { "node_modules/dotprompt": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/dotprompt/-/dotprompt-1.1.2.tgz", "resolved": "https://registry.npmjs.org/dotprompt/-/dotprompt-1.1.2.tgz",
-1
View File
@@ -19,7 +19,6 @@
"@google-cloud/firestore": "^7.11.6", "@google-cloud/firestore": "^7.11.6",
"@google-cloud/storage": "^7.17.1", "@google-cloud/storage": "^7.17.1",
"axios": "^1.11.0", "axios": "^1.11.0",
"dotenv": "^17.2.3",
"expo-server-sdk": "^4.0.0", "expo-server-sdk": "^4.0.0",
"ffmpeg-static": "^5.2.0", "ffmpeg-static": "^5.2.0",
"firebase-admin": "^12.6.0", "firebase-admin": "^12.6.0",
+58 -15
View File
@@ -1,6 +1,7 @@
const { onCall, HttpsError } = require("firebase-functions/v2/https"); const { onCall, HttpsError } = require("firebase-functions/v2/https");
const functions = require("firebase-functions"); const { defineSecret } = require("firebase-functions/params");
const logger = require("firebase-functions/logger"); const logger = require("firebase-functions/logger");
const functions = require("firebase-functions");
const admin = require("firebase-admin"); const admin = require("firebase-admin");
const axios = require("axios"); const axios = require("axios");
const fs = require("node:fs"); const fs = require("node:fs");
@@ -9,6 +10,15 @@ const os = require("node:os");
const path = require("node:path"); const path = require("node:path");
const { google } = require("googleapis"); const { google } = require("googleapis");
// ---- Secrets déclarés (gen2 + Secret Manager)
const S_YT_CLIENT_ID = defineSecret("YOUTUBE_CLIENT_ID");
const S_YT_CLIENT_SECRET = defineSecret("YOUTUBE_CLIENT_SECRET");
const S_YT_REFRESH_TOKEN = defineSecret("YOUTUBE_REFRESH_TOKEN");
const S_YT_REDIRECT_URI = defineSecret("YOUTUBE_REDIRECT_URI");
const S_YT_PRIVACY_STATUS = defineSecret("YOUTUBE_PRIVACY_STATUS");
const S_YT_CATEGORY_ID = defineSecret("YOUTUBE_CATEGORY_ID");
// Firestore
const firestore = admin.firestore(); const firestore = admin.firestore();
const projectsRef = firestore.collection("projects"); const projectsRef = firestore.collection("projects");
@@ -19,22 +29,47 @@ const YOUTUBE_IN_PROGRESS_STATUSES = [
"QUEUED", "QUEUED",
]; ];
const getEnvYoutubeConfig = () => // Lecture des secrets (recommandé en v2)
const getSecretsYoutubeConfig = () =>
Object.fromEntries( Object.fromEntries(
Object.entries({ Object.entries({
client_id: process.env.YOUTUBE_CLIENT_ID, client_id: S_YT_CLIENT_ID.value(),
client_secret: process.env.YOUTUBE_CLIENT_SECRET, client_secret: S_YT_CLIENT_SECRET.value(),
refresh_token: process.env.YOUTUBE_REFRESH_TOKEN, refresh_token: S_YT_REFRESH_TOKEN.value(),
redirect_uri: process.env.YOUTUBE_REDIRECT_URI, redirect_uri: S_YT_REDIRECT_URI.value(),
privacy_status: process.env.YOUTUBE_PRIVACY_STATUS, privacy_status: S_YT_PRIVACY_STATUS.value(),
category_id: process.env.YOUTUBE_CATEGORY_ID, category_id: S_YT_CATEGORY_ID.value(),
}).filter(([, value]) => value !== undefined && value !== "") }).filter(([, value]) => value !== undefined && value !== "")
); );
// Compat facultative v1 -> renverra {} en v2 (et on log un warn propre)
const getLegacyYoutubeConfig = () => {
if (typeof functions.config !== "function") {
return {};
}
try {
return functions.config()?.youtube || {};
} catch (error) {
if (
typeof error?.message === "string" &&
error.message.includes("functions.config() is no longer available")
) {
logger.warn(
"[publishPlaybackToYoutube] functions.config() indisponible, utilisation des secrets (Secret Manager)"
);
return {};
}
throw error;
}
};
const ensureYoutubeConfig = () => { const ensureYoutubeConfig = () => {
const firebaseConfig = functions.config()?.youtube || {}; // Fusionne (par prudence) lancienne config et les secrets actuels
const envConfig = getEnvYoutubeConfig(); const firebaseConfig = getLegacyYoutubeConfig();
const cfg = { ...firebaseConfig, ...envConfig }; const secretConfig = getSecretsYoutubeConfig();
const cfg = { ...firebaseConfig, ...secretConfig };
const requiredKeys = ["client_id", "client_secret", "refresh_token"]; const requiredKeys = ["client_id", "client_secret", "refresh_token"];
const missing = requiredKeys.filter((key) => !cfg[key]); const missing = requiredKeys.filter((key) => !cfg[key]);
if (missing.length) { if (missing.length) {
@@ -48,10 +83,9 @@ const ensureYoutubeConfig = () => {
clientId: cfg.client_id, clientId: cfg.client_id,
clientSecret: cfg.client_secret, clientSecret: cfg.client_secret,
refreshToken: cfg.refresh_token, refreshToken: cfg.refresh_token,
redirectUri: redirectUri: cfg.redirect_uri,
cfg.redirect_uri || "https://developers.google.com/oauthplayground", defaultPrivacyStatus: cfg.privacy_status,
defaultPrivacyStatus: cfg.privacy_status || "unlisted", defaultCategoryId: cfg.category_id,
defaultCategoryId: cfg.category_id || "10",
}; };
}; };
@@ -130,6 +164,15 @@ exports.publishPlaybackToYoutube = onCall(
"https://musicland-one.vercel.app/", "https://musicland-one.vercel.app/",
"https://musicland-d33f9.firebaseapp.com", "https://musicland-d33f9.firebaseapp.com",
], ],
// Secrets requis pour lexécution (v2)
secrets: [
S_YT_CLIENT_ID,
S_YT_CLIENT_SECRET,
S_YT_REFRESH_TOKEN,
S_YT_REDIRECT_URI,
S_YT_PRIVACY_STATUS,
S_YT_CATEGORY_ID,
],
}, },
async ({ data = {}, auth }) => { async ({ data = {}, auth }) => {
const uid = auth?.uid; const uid = auth?.uid;
+1 -1
View File
@@ -111,7 +111,7 @@ const PublishYoutube = () => {
try { try {
const publishCallable = firebase const publishCallable = firebase
.functions() .functions()
.httpsCallable("publishPlaybackToYoutube"); .httpsCallable("youtube-publishPlaybackToYoutube");
await publishCallable({ projectId }); await publishCallable({ projectId });
setTooltip?.({ setTooltip?.({
type: "success", type: "success",
-24
View File
@@ -1,24 +0,0 @@
https://accounts.google.com/o/oauth2/v2/auth
?client_id=305598753437-21ivo6din58aud9dph9bcob06fb52r5n.apps.googleusercontent.com
&redirect_uri=http://localhost:8081
&response_type=code
&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.upload
&access_type=offline
&prompt=consent
http://localhost:8081/?code=4/0Ab32j90LMDeJDShWSk1OkXwgbjrHS-qQpyYTlXhH9q3nlV0EJa6cTL-7nI9yOKHoz6rgRA&scope=https://www.googleapis.com/auth/youtube.upload
curl -X POST https://oauth2.googleapis.com/token \
-d client_id=305598753437-21ivo6din58aud9dph9bcob06fb52r5n.apps.googleusercontent.com \
-d client_secret=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 \
-d code=4/0Ab32j90LMDeJDShWSk1OkXwgbjrHS-qQpyYTlXhH9q3nlV0EJa6cTL-7nI9yOKHoz6rgRA \
-d grant_type=authorization_code \
-d redirect_uri=http://localhost:8081
{
"access_token": "ya29.a0ATi6K2uR4DhnmTDl0EN2tvDogwwEwl8SDJLpUzvnZ8LGgOYjgmhqg7MoMM0_iTJR_NBEDPe2LlJDqCFFnIi9s3qRv_zfw_NeTnN-LwUmAyVJkdqSP6GvzQCuZkE6OBuTvaU5b2obk5rHvDOkWXxAb7xjbVTzIBIg8O5VVhJ-VB8VEbTVsSq9bc0ljkbcyBEzm4JdQRwaCgYKAcgSAQ8SFQHGX2MiR1Owf2aiiEBUsU-LDUDgWg0206",
"expires_in": 3599,
"refresh_token": "1//03sy-LDVmCMcFCgYIARAAGAMSNwF-L9IrHwwdBBZvg3Hwun9hxHUx_AsJaa1qeozmtvGtdOXKIRIyKjTp7S_b0igvzM_1A5bLH34",
"scope": "https://www.googleapis.com/auth/youtube.upload",
"token_type": "Bearer"
}