feat: test publish to youtube
This commit is contained in:
@@ -40,6 +40,12 @@ const STAGE_CARD_CONTENT = [
|
|||||||
description: "Come back when your audio is ready!",
|
description: "Come back when your audio is ready!",
|
||||||
image: ai.rightIcon,
|
image: ai.rightIcon,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "publisher",
|
||||||
|
title: "Bena",
|
||||||
|
description: "Ta vidéo est prête ? Direction YouTube !",
|
||||||
|
image: ai.bena,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const WEB_SCROLL_INACTIVE_DELTA = 0.05;
|
const WEB_SCROLL_INACTIVE_DELTA = 0.05;
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ const STAGE_CARD_CONTENT = [
|
|||||||
description: "Come back when your audio is ready!",
|
description: "Come back when your audio is ready!",
|
||||||
image: ai.rightIcon,
|
image: ai.rightIcon,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "publisher",
|
||||||
|
title: "Bena",
|
||||||
|
description: "Ta vidéo est prête ? Direction YouTube !",
|
||||||
|
image: ai.bena,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const WEB_SCROLL_INACTIVE_DELTA = 0.05;
|
const WEB_SCROLL_INACTIVE_DELTA = 0.05;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import PlaybackOnboarding from "../screens/Playback/PlaybackOnboarding";
|
|||||||
import RecordPlayback from "../screens/Playback/RecordPlayback";
|
import RecordPlayback from "../screens/Playback/RecordPlayback";
|
||||||
import RecordedPlayback from "../screens/Playback/RecordedPlayback";
|
import RecordedPlayback from "../screens/Playback/RecordedPlayback";
|
||||||
import VideoFinalize from "../screens/Playback/VideoFinalize";
|
import VideoFinalize from "../screens/Playback/VideoFinalize";
|
||||||
|
import PublishYoutube from "../screens/Publishing/PublishYoutube";
|
||||||
import PrivacyPolicy from "../screens/PrivacyPolicy";
|
import PrivacyPolicy from "../screens/PrivacyPolicy";
|
||||||
import DownloadPrices from "../screens/Production/DownloadPrices";
|
import DownloadPrices from "../screens/Production/DownloadPrices";
|
||||||
import DownloadSongs from "../screens/Production/DownloadSongs";
|
import DownloadSongs from "../screens/Production/DownloadSongs";
|
||||||
@@ -228,6 +229,10 @@ const baseScreens = [
|
|||||||
name: Routes.VideoFinalize,
|
name: Routes.VideoFinalize,
|
||||||
component: VideoFinalize,
|
component: VideoFinalize,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: Routes.PublishYoutube,
|
||||||
|
component: PublishYoutube,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: Routes.AllMyPlaylist,
|
name: Routes.AllMyPlaylist,
|
||||||
component: AllMyPlaylist,
|
component: AllMyPlaylist,
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export const Routes = {
|
|||||||
ChooseDecor: "ChooseDecor",
|
ChooseDecor: "ChooseDecor",
|
||||||
CreatingDecor: "CreatingDecor",
|
CreatingDecor: "CreatingDecor",
|
||||||
VideoFinalize: "VideoFinalize",
|
VideoFinalize: "VideoFinalize",
|
||||||
|
PublishYoutube: "PublishYoutube",
|
||||||
|
|
||||||
Create: "Create",
|
Create: "Create",
|
||||||
HitParade: "HitParade",
|
HitParade: "HitParade",
|
||||||
|
|||||||
@@ -36,6 +36,14 @@ const CREATE_DATA = [
|
|||||||
desc: "Come back when your\naudio is ready!",
|
desc: "Come back when your\naudio is ready!",
|
||||||
type: "Director",
|
type: "Director",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
stageKey: "publisher",
|
||||||
|
img: ai.bena,
|
||||||
|
bg: background.productionBG2,
|
||||||
|
label: "Bena",
|
||||||
|
desc: "Publions ta vidéo sur YouTube !",
|
||||||
|
type: "Producteur",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const NewMusicOptions = () => {
|
const NewMusicOptions = () => {
|
||||||
|
|||||||
@@ -0,0 +1,261 @@
|
|||||||
|
import { useRoute } from "@react-navigation/native";
|
||||||
|
import { Image } from "expo-image";
|
||||||
|
import React, { useCallback, useMemo, useState } from "react";
|
||||||
|
import { Linking, Text, View } from "react-native";
|
||||||
|
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
|
||||||
|
import { ai, background } from "../../assets";
|
||||||
|
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||||
|
import GradientButton from "../../components/GradientButton";
|
||||||
|
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||||
|
import firebase, { projectsRef, serverTimestamp } from "../../config/firebase";
|
||||||
|
import Page from "../../layouts/Page";
|
||||||
|
import { goBack } from "../../navigation/NavigationService";
|
||||||
|
import { useUser } from "../../providers/UserDataProvider";
|
||||||
|
import { Palette, gutters } from "../../styles";
|
||||||
|
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||||
|
|
||||||
|
const PublishYoutube = () => {
|
||||||
|
const route = useRoute();
|
||||||
|
const routeProjectId = route?.params?.projectId ?? null;
|
||||||
|
const { userProjects = [], selectedProject } = useUser();
|
||||||
|
const { setTooltip } = useMinuit();
|
||||||
|
|
||||||
|
const project = useMemo(() => {
|
||||||
|
if (routeProjectId) {
|
||||||
|
return (
|
||||||
|
userProjects.find((item) => item?.id === routeProjectId) ||
|
||||||
|
selectedProject ||
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (selectedProject?.id) {
|
||||||
|
return selectedProject;
|
||||||
|
}
|
||||||
|
return userProjects.length ? userProjects[0] : null;
|
||||||
|
}, [routeProjectId, selectedProject, userProjects]);
|
||||||
|
|
||||||
|
const projectId = project?.id || routeProjectId || null;
|
||||||
|
|
||||||
|
const [saving, setSaving] = useState(false);
|
||||||
|
const youtubeUrl = project?.youtubeUrl ?? null;
|
||||||
|
const youtubeStatus = project?.youtubeStatus ?? null;
|
||||||
|
const youtubeError = project?.youtubeError ?? null;
|
||||||
|
const playbackReady = !!project?.playbackUrl;
|
||||||
|
const hasYoutubePublication = !!youtubeUrl;
|
||||||
|
const isPublishing = useMemo(
|
||||||
|
() =>
|
||||||
|
["PUBLISHING", "UPLOADING", "PROCESSING", "QUEUED"].includes(
|
||||||
|
youtubeStatus
|
||||||
|
),
|
||||||
|
[youtubeStatus]
|
||||||
|
);
|
||||||
|
const publishDisabled =
|
||||||
|
saving || isPublishing || !playbackReady || !projectId;
|
||||||
|
const publishButtonLabel = isPublishing
|
||||||
|
? "Publication en cours..."
|
||||||
|
: hasYoutubePublication
|
||||||
|
? "Mettre à jour sur YouTube"
|
||||||
|
: "Publier sur YouTube";
|
||||||
|
const statusMessage = useMemo(() => {
|
||||||
|
if (!playbackReady) {
|
||||||
|
return "Termine la création de ton playback avec John pour débloquer la publication YouTube.";
|
||||||
|
}
|
||||||
|
if (isPublishing) {
|
||||||
|
return "Bena s'occupe de mettre ta vidéo en ligne... Patiente un instant.";
|
||||||
|
}
|
||||||
|
if (youtubeError || youtubeStatus === "FAILED") {
|
||||||
|
return "La dernière tentative de publication a échoué. Tu peux réessayer ci-dessous.";
|
||||||
|
}
|
||||||
|
if (hasYoutubePublication) {
|
||||||
|
return "Ta vidéo est publiée sur la chaîne YouTube MusicLand. Tu peux la partager dès maintenant.";
|
||||||
|
}
|
||||||
|
return "Bena t’accompagne pour partager ton playback sur la chaîne YouTube MusicLand.";
|
||||||
|
}, [
|
||||||
|
hasYoutubePublication,
|
||||||
|
isPublishing,
|
||||||
|
playbackReady,
|
||||||
|
youtubeError,
|
||||||
|
youtubeStatus,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleOpenYoutube = useCallback(async () => {
|
||||||
|
if (!youtubeUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await Linking.openURL(youtubeUrl);
|
||||||
|
} catch (_error) {
|
||||||
|
setTooltip?.({
|
||||||
|
type: "error",
|
||||||
|
text: "Impossible d'ouvrir le lien YouTube",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [setTooltip, youtubeUrl]);
|
||||||
|
|
||||||
|
const handleMarkPublished = useCallback(async () => {
|
||||||
|
if (!projectId) {
|
||||||
|
setTooltip?.({
|
||||||
|
type: "error",
|
||||||
|
text: "Sélectionnez un projet avant de publier",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!playbackReady) {
|
||||||
|
setTooltip?.({
|
||||||
|
type: "error",
|
||||||
|
text: "Aucun playback n'est disponible pour la publication",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSaving(true);
|
||||||
|
try {
|
||||||
|
const publishCallable = firebase
|
||||||
|
.functions()
|
||||||
|
.httpsCallable("publishPlaybackToYoutube");
|
||||||
|
await publishCallable({ projectId });
|
||||||
|
setTooltip?.({
|
||||||
|
type: "success",
|
||||||
|
text: "Publication lancée sur YouTube",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
setTooltip?.({
|
||||||
|
type: "error",
|
||||||
|
text: error?.message || "Impossible de lancer la publication",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
}, [playbackReady, projectId, setTooltip]);
|
||||||
|
|
||||||
|
const handleResetPublication = useCallback(async () => {
|
||||||
|
if (!projectId) {
|
||||||
|
setTooltip?.({
|
||||||
|
type: "error",
|
||||||
|
text: "Sélectionnez un projet avant de réinitialiser",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSaving(true);
|
||||||
|
try {
|
||||||
|
await projectsRef.doc(projectId).set(
|
||||||
|
{
|
||||||
|
youtubePublished: false,
|
||||||
|
youtubeUrl: null,
|
||||||
|
youtubeVideoId: null,
|
||||||
|
youtubePublishedAt: null,
|
||||||
|
youtubeStatus: "IDLE",
|
||||||
|
youtubeError: null,
|
||||||
|
updatedAt: serverTimestamp(),
|
||||||
|
},
|
||||||
|
{ merge: true }
|
||||||
|
);
|
||||||
|
setTooltip?.({
|
||||||
|
type: "success",
|
||||||
|
text: "Publication YouTube réinitialisée",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
setTooltip?.({
|
||||||
|
type: "error",
|
||||||
|
text: error?.message || "Réinitialisation impossible",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
}, [projectId, setTooltip]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Page backgroundImg={background.playbackBG2} headerType="NONE">
|
||||||
|
<MusicLandHeader progress={100} onPressBack={goBack} />
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
paddingHorizontal: gutters,
|
||||||
|
paddingBottom: gutters * 2,
|
||||||
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View style={{ alignItems: "center", gap: 18 }}>
|
||||||
|
<Image
|
||||||
|
source={ai.bena}
|
||||||
|
style={{ width: 220, height: 320 }}
|
||||||
|
contentFit="contain"
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontSize: 22,
|
||||||
|
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||||
|
color: Palette.white,
|
||||||
|
textAlign: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{hasYoutubePublication
|
||||||
|
? "Ta vidéo est déjà sur YouTube"
|
||||||
|
: "Publier sur YouTube"}
|
||||||
|
</Text>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: FONT_FAMILY.InterRegular,
|
||||||
|
color: Palette.white,
|
||||||
|
opacity: 0.9,
|
||||||
|
textAlign: "center",
|
||||||
|
lineHeight: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{statusMessage}
|
||||||
|
</Text>
|
||||||
|
{youtubeError && (
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: FONT_FAMILY.InterRegular,
|
||||||
|
color: Palette.red,
|
||||||
|
textAlign: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{youtubeError}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{hasYoutubePublication && youtubeUrl && (
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontSize: 13,
|
||||||
|
fontFamily: FONT_FAMILY.InterMedium,
|
||||||
|
color: Palette.white,
|
||||||
|
textAlign: "center",
|
||||||
|
opacity: 0.8,
|
||||||
|
}}
|
||||||
|
numberOfLines={2}
|
||||||
|
>
|
||||||
|
{youtubeUrl}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
<View style={{ gap: 14 }}>
|
||||||
|
<GradientButton
|
||||||
|
title={publishButtonLabel}
|
||||||
|
onPress={handleMarkPublished}
|
||||||
|
disabled={publishDisabled}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{hasYoutubePublication && youtubeUrl && (
|
||||||
|
<BorderGradientButton
|
||||||
|
title="Voir la vidéo sur YouTube"
|
||||||
|
onPress={handleOpenYoutube}
|
||||||
|
disabled={saving || isPublishing}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{hasYoutubePublication && (
|
||||||
|
<BorderGradientButton
|
||||||
|
title="Réinitialiser la publication"
|
||||||
|
onPress={handleResetPublication}
|
||||||
|
disabled={saving || isPublishing}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Page>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PublishYoutube;
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
import { Routes } from "../navigation/Routes";
|
import { Routes } from "../navigation/Routes";
|
||||||
|
|
||||||
export const CREATION_STAGE_KEYS = ["songwriter", "beatmaker", "director"];
|
export const CREATION_STAGE_KEYS = [
|
||||||
|
"songwriter",
|
||||||
|
"beatmaker",
|
||||||
|
"director",
|
||||||
|
"publisher",
|
||||||
|
];
|
||||||
|
|
||||||
const getLyricsCount = (project) => {
|
const getLyricsCount = (project) => {
|
||||||
if (!project) return 0;
|
if (!project) return 0;
|
||||||
@@ -16,6 +21,16 @@ const getStageMetadata = (project) => {
|
|||||||
const hasCoverUrl = !!project?.coverUrl;
|
const hasCoverUrl = !!project?.coverUrl;
|
||||||
const hasPlaybackAsset = !!project?.playbackUrl;
|
const hasPlaybackAsset = !!project?.playbackUrl;
|
||||||
const hasPlayback = hasPlaybackAsset || hasSongUrl;
|
const hasPlayback = hasPlaybackAsset || hasSongUrl;
|
||||||
|
const youtubeUrl = project?.youtubeUrl ?? null;
|
||||||
|
const youtubeStatus = project?.youtubeStatus ?? null;
|
||||||
|
const youtubeError = project?.youtubeError ?? null;
|
||||||
|
const hasYoutubePublication = !!youtubeUrl;
|
||||||
|
const isYoutubePublishing = [
|
||||||
|
"PUBLISHING",
|
||||||
|
"UPLOADING",
|
||||||
|
"PROCESSING",
|
||||||
|
"QUEUED",
|
||||||
|
].includes(youtubeStatus);
|
||||||
const musicUrls = Array.isArray(project?.musicUrls)
|
const musicUrls = Array.isArray(project?.musicUrls)
|
||||||
? project.musicUrls.filter(Boolean)
|
? project.musicUrls.filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
@@ -38,9 +53,13 @@ const getStageMetadata = (project) => {
|
|||||||
musicStatus,
|
musicStatus,
|
||||||
coverStatus,
|
coverStatus,
|
||||||
playbackStatus,
|
playbackStatus,
|
||||||
|
youtubeStatus,
|
||||||
|
youtubeError,
|
||||||
isMusicGenerating: musicStatus === "GENERATING",
|
isMusicGenerating: musicStatus === "GENERATING",
|
||||||
isCoverGenerating: coverStatus === "GENERATING",
|
isCoverGenerating: coverStatus === "GENERATING",
|
||||||
isPlaybackGenerating,
|
isPlaybackGenerating,
|
||||||
|
hasYoutubePublication,
|
||||||
|
isYoutubePublishing,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,6 +74,8 @@ const getStageLockState = (key, metadata) => {
|
|||||||
return metadata.lyricsCount <= 0;
|
return metadata.lyricsCount <= 0;
|
||||||
case "director":
|
case "director":
|
||||||
return !metadata.hasCover;
|
return !metadata.hasCover;
|
||||||
|
case "publisher":
|
||||||
|
return !metadata.hasPlaybackAsset;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -111,6 +132,20 @@ const getStageDescription = (key, metadata) => {
|
|||||||
return "Modifier le playback généré";
|
return "Modifier le playback généré";
|
||||||
}
|
}
|
||||||
return "Commencer la création de votre playback";
|
return "Commencer la création de votre playback";
|
||||||
|
case "publisher":
|
||||||
|
if (!hasPlaybackAsset) {
|
||||||
|
return "Créez un playback pour débloquer la publication";
|
||||||
|
}
|
||||||
|
if (metadata.isYoutubePublishing) {
|
||||||
|
return "Publication de votre vidéo en cours";
|
||||||
|
}
|
||||||
|
if (metadata.youtubeError) {
|
||||||
|
return "La publication a échoué, réessayez.";
|
||||||
|
}
|
||||||
|
if (metadata.hasYoutubePublication) {
|
||||||
|
return "Votre vidéo est en ligne et prête à être partagée";
|
||||||
|
}
|
||||||
|
return "Publier votre vidéo sur la chaîne YouTube MusicLand";
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -133,6 +168,13 @@ const getStageLockedDescription = (key, metadata) => {
|
|||||||
return metadata.hasPlaybackAsset
|
return metadata.hasPlaybackAsset
|
||||||
? "Impossible de modifier le playback"
|
? "Impossible de modifier le playback"
|
||||||
: undefined;
|
: undefined;
|
||||||
|
case "publisher":
|
||||||
|
if (!metadata.hasPlaybackAsset) {
|
||||||
|
return "Générez un playback pour débloquer la publication";
|
||||||
|
}
|
||||||
|
return metadata.hasYoutubePublication
|
||||||
|
? "La vidéo est déjà publiée"
|
||||||
|
: undefined;
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -146,6 +188,8 @@ const getStageCompletionState = (key, metadata) => {
|
|||||||
return metadata.hasSongUrl || metadata.musicStatus === "GENERATED";
|
return metadata.hasSongUrl || metadata.musicStatus === "GENERATED";
|
||||||
case "director":
|
case "director":
|
||||||
return metadata.hasPlaybackAsset;
|
return metadata.hasPlaybackAsset;
|
||||||
|
case "publisher":
|
||||||
|
return metadata.hasYoutubePublication;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -224,6 +268,15 @@ export const getStageAction = (key, project) => {
|
|||||||
route: Routes.Playback,
|
route: Routes.Playback,
|
||||||
params: project ? { project } : undefined,
|
params: project ? { project } : undefined,
|
||||||
};
|
};
|
||||||
|
case "publisher": {
|
||||||
|
if (!project?.id) {
|
||||||
|
return { route: Routes.PublishYoutube };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
route: Routes.PublishYoutube,
|
||||||
|
params: { projectId: project.id },
|
||||||
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
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"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user