Skip to content
This repository was archived by the owner on Apr 13, 2025. It is now read-only.

Commit 70314dd

Browse files
authored
Merge pull request #214 from codeoverflow-org/enhancement/youtube-save-refresh-token
Save youtube refresh token in config for re-use
2 parents 9e0d1ec + 56a8c6f commit 70314dd

File tree

5 files changed

+53
-12
lines changed

5 files changed

+53
-12
lines changed

nodecg-io-core/extension/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module.exports = (nodecg: NodeCG): NodeCGIOCore => {
3232
persistenceManager,
3333
).registerMessageHandlers();
3434

35-
registerExitHandlers(nodecg, bundleManager, instanceManager, serviceManager);
35+
registerExitHandlers(nodecg, bundleManager, instanceManager, serviceManager, persistenceManager);
3636

3737
// We use a extra object instead of returning a object containing all the managers and so on, because
3838
// any loaded bundle would be able to call any (public or private) of the managers which is not intended.
@@ -62,7 +62,12 @@ function onExit(
6262
bundleManager: BundleManager,
6363
instanceManager: InstanceManager,
6464
serviceManager: ServiceManager,
65+
persistenceManager: PersistenceManager,
6566
): void {
67+
// Save everything
68+
// This is especially important if some services update some configs (e.g. updated tokens) and they haven't been saved yet.
69+
persistenceManager.save();
70+
6671
// Unset all service instances in all bundles
6772
const bundles = bundleManager.getBundleDependencies();
6873
for (const bundleName in bundles) {
@@ -99,9 +104,10 @@ function registerExitHandlers(
99104
bundleManager: BundleManager,
100105
instanceManager: InstanceManager,
101106
serviceManager: ServiceManager,
107+
persistenceManager: PersistenceManager,
102108
): void {
103109
const handler = () => {
104-
onExit(nodecg, bundleManager, instanceManager, serviceManager);
110+
onExit(nodecg, bundleManager, instanceManager, serviceManager, persistenceManager);
105111
};
106112

107113
// Normal exit

nodecg-io-core/extension/persistenceManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ export class PersistenceManager {
208208
/**
209209
* Encrypts and saves current state to the persistent replicant.
210210
*/
211-
private save() {
211+
save(): void {
212212
// Check if we have a password to encrypt the data with.
213213
if (this.password === undefined) {
214214
return;

nodecg-io-spotify/extension/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class SpotifyService extends ServiceBundle<SpotifyServiceConfig, SpotifyServiceC
9595

9696
// This little snippet closes the oauth window after the connection was successful
9797
const callbackWebsite =
98-
"<http><head><script>window.close();</script></head><body>Spotify connection successful! You may close this window now.</body></http>";
98+
"<html><head><script>window.close();</script></head><body>Spotify connection successful! You may close this window now.</body></html>";
9999
res.send(callbackWebsite);
100100
});
101101

nodecg-io-youtube/extension/index.ts

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { NodeCG } from "nodecg/types/server";
22
import { Result, emptySuccess, success, error, ServiceBundle } from "nodecg-io-core";
33
import { google, youtube_v3 } from "googleapis";
4+
import type { Credentials } from "google-auth-library/build/src/auth/credentials";
5+
import type { OAuth2Client } from "google-auth-library/build/src/auth/oauth2client";
46
import * as express from "express";
57
import opn = require("open");
68

79
interface YoutubeServiceConfig {
810
clientID: string;
911
clientSecret: string;
12+
refreshToken?: string;
1013
}
1114

1215
export type YoutubeServiceClient = youtube_v3.Youtube;
@@ -26,6 +29,36 @@ class YoutubeService extends ServiceBundle<YoutubeServiceConfig, YoutubeServiceC
2629
clientSecret: config.clientSecret,
2730
redirectUri: "http://localhost:9090/nodecg-io-youtube/oauth2callback",
2831
});
32+
33+
if (config.refreshToken) {
34+
this.nodecg.log.info("Re-using saved refresh token.");
35+
auth.setCredentials({
36+
refresh_token: config.refreshToken,
37+
});
38+
} else {
39+
this.nodecg.log.info("No refresh token found. Starting auth flow to get one...");
40+
auth.setCredentials(await this.initialAuth(auth));
41+
if (auth.credentials.refresh_token) {
42+
config.refreshToken = auth.credentials.refresh_token;
43+
}
44+
}
45+
46+
// Save refresh tokens so they can be used next time to get a access token again
47+
auth.on("tokens", (tokens) => {
48+
if (tokens.refresh_token) {
49+
config.refreshToken = tokens.refresh_token;
50+
}
51+
});
52+
53+
const client = new youtube_v3.Youtube({ auth });
54+
return success(client);
55+
}
56+
57+
stopClient(_client: YoutubeServiceClient): void {
58+
// Cannot stop client
59+
}
60+
61+
private initialAuth(auth: OAuth2Client): Promise<Credentials> {
2962
const authUrl = auth.generateAuthUrl({
3063
access_type: "offline",
3164
scope: "https://www.googleapis.com/auth/youtube",
@@ -38,12 +71,14 @@ class YoutubeService extends ServiceBundle<YoutubeServiceConfig, YoutubeServiceC
3871
router.get("/nodecg-io-youtube/oauth2callback", async (req, res) => {
3972
try {
4073
const params = req.query;
41-
res.end("<script>window.close()</script>");
74+
75+
const callbackWebsite =
76+
"<html><head><script>window.close();</script></head><body>YouTube connection successful! You may close this window now.</body></html>";
77+
res.send(callbackWebsite);
78+
4279
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
4380
const { tokens } = await auth.getToken(params.code!.toString());
44-
auth.credentials = tokens;
45-
const client = new youtube_v3.Youtube({ auth });
46-
resolve(success(client));
81+
resolve(tokens);
4782
} catch (e) {
4883
reject(error(e));
4984
}
@@ -53,8 +88,4 @@ class YoutubeService extends ServiceBundle<YoutubeServiceConfig, YoutubeServiceC
5388
opn(authUrl, { wait: false }).then((cp) => cp.unref());
5489
});
5590
}
56-
57-
stopClient(_client: YoutubeServiceClient): void {
58-
// Cannot stop client
59-
}
6091
}

nodecg-io-youtube/youtube-schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
"clientSecret": {
1111
"type": "string",
1212
"description": "The oauth client secret https://console.cloud.google.com/apis/credentials/oauthclient"
13+
},
14+
"refreshToken": {
15+
"type": "string",
16+
"description": "Token that allows the client to refresh access tokens. This is set automatically after first login, you don't need to set it."
1317
}
1418
},
1519
"required": ["clientID", "clientSecret"]

0 commit comments

Comments
 (0)