diff --git a/nodecg-io-core/extension/index.ts b/nodecg-io-core/extension/index.ts index 3ec9b6d5f..2bebe8fa1 100644 --- a/nodecg-io-core/extension/index.ts +++ b/nodecg-io-core/extension/index.ts @@ -32,7 +32,7 @@ module.exports = (nodecg: NodeCG): NodeCGIOCore => { persistenceManager, ).registerMessageHandlers(); - registerExitHandlers(nodecg, bundleManager, instanceManager, serviceManager); + registerExitHandlers(nodecg, bundleManager, instanceManager, serviceManager, persistenceManager); // We use a extra object instead of returning a object containing all the managers and so on, because // 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( bundleManager: BundleManager, instanceManager: InstanceManager, serviceManager: ServiceManager, + persistenceManager: PersistenceManager, ): void { + // Save everything + // This is especially important if some services update some configs (e.g. updated tokens) and they haven't been saved yet. + persistenceManager.save(); + // Unset all service instances in all bundles const bundles = bundleManager.getBundleDependencies(); for (const bundleName in bundles) { @@ -99,9 +104,10 @@ function registerExitHandlers( bundleManager: BundleManager, instanceManager: InstanceManager, serviceManager: ServiceManager, + persistenceManager: PersistenceManager, ): void { const handler = () => { - onExit(nodecg, bundleManager, instanceManager, serviceManager); + onExit(nodecg, bundleManager, instanceManager, serviceManager, persistenceManager); }; // Normal exit diff --git a/nodecg-io-core/extension/persistenceManager.ts b/nodecg-io-core/extension/persistenceManager.ts index 56543909a..c709baba4 100644 --- a/nodecg-io-core/extension/persistenceManager.ts +++ b/nodecg-io-core/extension/persistenceManager.ts @@ -208,7 +208,7 @@ export class PersistenceManager { /** * Encrypts and saves current state to the persistent replicant. */ - private save() { + save(): void { // Check if we have a password to encrypt the data with. if (this.password === undefined) { return; diff --git a/nodecg-io-spotify/extension/index.ts b/nodecg-io-spotify/extension/index.ts index cb11562ef..11092a893 100644 --- a/nodecg-io-spotify/extension/index.ts +++ b/nodecg-io-spotify/extension/index.ts @@ -95,7 +95,7 @@ class SpotifyService extends ServiceBundleSpotify connection successful! You may close this window now."; + "Spotify connection successful! You may close this window now."; res.send(callbackWebsite); }); diff --git a/nodecg-io-youtube/extension/index.ts b/nodecg-io-youtube/extension/index.ts index ec64cc495..757e6c5fd 100644 --- a/nodecg-io-youtube/extension/index.ts +++ b/nodecg-io-youtube/extension/index.ts @@ -1,12 +1,15 @@ import { NodeCG } from "nodecg/types/server"; import { Result, emptySuccess, success, error, ServiceBundle } from "nodecg-io-core"; import { google, youtube_v3 } from "googleapis"; +import type { Credentials } from "google-auth-library/build/src/auth/credentials"; +import type { OAuth2Client } from "google-auth-library/build/src/auth/oauth2client"; import * as express from "express"; import opn = require("open"); interface YoutubeServiceConfig { clientID: string; clientSecret: string; + refreshToken?: string; } export type YoutubeServiceClient = youtube_v3.Youtube; @@ -26,6 +29,36 @@ class YoutubeService extends ServiceBundle { + if (tokens.refresh_token) { + config.refreshToken = tokens.refresh_token; + } + }); + + const client = new youtube_v3.Youtube({ auth }); + return success(client); + } + + stopClient(_client: YoutubeServiceClient): void { + // Cannot stop client + } + + private initialAuth(auth: OAuth2Client): Promise { const authUrl = auth.generateAuthUrl({ access_type: "offline", scope: "https://www.googleapis.com/auth/youtube", @@ -38,12 +71,14 @@ class YoutubeService extends ServiceBundle { try { const params = req.query; - res.end(""); + + const callbackWebsite = + "YouTube connection successful! You may close this window now."; + res.send(callbackWebsite); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const { tokens } = await auth.getToken(params.code!.toString()); - auth.credentials = tokens; - const client = new youtube_v3.Youtube({ auth }); - resolve(success(client)); + resolve(tokens); } catch (e) { reject(error(e)); } @@ -53,8 +88,4 @@ class YoutubeService extends ServiceBundle cp.unref()); }); } - - stopClient(_client: YoutubeServiceClient): void { - // Cannot stop client - } } diff --git a/nodecg-io-youtube/youtube-schema.json b/nodecg-io-youtube/youtube-schema.json index 66cc8c516..3cc2be27a 100644 --- a/nodecg-io-youtube/youtube-schema.json +++ b/nodecg-io-youtube/youtube-schema.json @@ -10,6 +10,10 @@ "clientSecret": { "type": "string", "description": "The oauth client secret https://console.cloud.google.com/apis/credentials/oauthclient" + }, + "refreshToken": { + "type": "string", + "description": "Token that allows the client to refresh access tokens. This is set automatically after first login, you don't need to set it." } }, "required": ["clientID", "clientSecret"]