import express from 'express'; import { google } from 'googleapis'; import open from 'open'; import fs from 'fs'; import path from 'path'; import { tokenManager } from './tokenManager.js'; (await import('dotenv')).config({ path: './Downloader/secret/config.env', debug: true }); const app = express(); const port = 3000; const { CLIENT_ID, CLIENT_SECRET, REDIRECT_URI } = process.env, manager = new tokenManager({ clientId: CLIENT_ID, clientSecret: CLIENT_SECRET, redirectUri: REDIRECT_URI, tokenPath: 'Downloader/secret/token.json' }); const oauth2Client = manager.getAuthClient(); // scope to read playlist items/liked videos const SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']; let downloadStatus = 'idle'; // can be: 'idle', 'in-progress', 'completed', 'error' //#region oauth flow app.get('/auth', async (_req, res) => { const t = manager.loadToken(); if (t) return res.redirect('/choose-playlist'); // generate auth url const authUrl = oauth2Client.generateAuthUrl({ access_type: 'offline', scope: SCOPES }); // automatically open the url in the default browser const c = await open(authUrl).catch((err) => { console.error('error opening browser:', err); return res.status(500).send('failed to open browser for oauth.'); }); c.on('close', () => res.redirect('/choose-playlist')) }); app.get('/oauth2callback', async (req, res) => { try { const code = req.query.code; const { tokens } = await oauth2Client.getToken(code); oauth2Client.setCredentials(tokens); manager.saveToken(tokens); // close the window res.sendStatus(200); } catch (err) { console.error('error retrieving token:', err); res.status(500).send('error retrieving token.'); } }); //#endregion //#region youtube stuffs async function getAllPlaylists(auth) { const youtube = google.youtube('v3'); let playlists = []; let nextPageToken = null; do { const response = await youtube.playlists.list({ auth, part: 'snippet', mine: true, maxResults: 50, pageToken: nextPageToken }); if (response.data.items) { playlists = playlists.concat(response.data.items); } nextPageToken = response.data.nextPageToken; } while (nextPageToken); return playlists; } async function getPlaylistItems(playlistId, auth) { const youtube = google.youtube('v3'); let items = []; let nextPageToken = null; do { const response = await youtube.playlistItems.list({ auth, part: 'snippet,contentDetails', playlistId, maxResults: 50, pageToken: nextPageToken }); if (response.data.items) { items = items.concat(response.data.items); } nextPageToken = response.data.nextPageToken; } while (nextPageToken); return items.map(o => `https://music.youtube.com/watch?v=${o.id}`); } //#endregion //#region routes app.get('/choose-playlist', async (_req, res) => { try { if (!oauth2Client.credentials || !oauth2Client.credentials.access_token) { const t = manager.loadToken(); if (!t) return res.redirect('/auth'); } const playlists = await getAllPlaylists(oauth2Client); let html = `
downloaded ${items.length} items to ${outFile}
`); } catch (err) { console.error('error downloading playlist:', err); downloadStatus = 'error'; res.status(500).send('error downloading playlist.'); } }); app.get('/status', (_req, res) => { let html = `