import chromeLocation from 'chrome-location' ; import * as Path from 'path' ; import * as fs from 'fs/promises' ; import * as os from 'os' ; import { spawn } from 'child_process' ; import { pid2title , URL , makeWebDriver , Cookies } from '../helpers' ; import { until , WebDriver } from 'selenium-webdriver' ; const delayAsync = ( ms ) => new Promise ( ( res ) => setTimeout ( res , ms ) ) ; const isRunning = ( pid ) : boolean => { try { return process . kill ( pid , 0 ) ; } catch ( error ) { return error . code === 'EPERM' ; } } ; interface ITempDirectory { path : string ; remove : ( ) => Promise < void > ; } const createTmpDir = async ( prefix ) : Promise < ITempDirectory > => { const path = await fs . mkdtemp ( Path . join ( os . tmpdir ( ) , prefix ) ) ; const remove = ( ) => fs . rm ( path , { recursive : true , force : true , maxRetries : 3 } ) ; return { path , remove } ; } ; const chromePreventCrashDialog = async ( profilePath ) : Promise < void > => { const preferencesPath = Path . join ( profilePath , 'Default' , 'Preferences' ) ; const jsonBuf = await fs . readFile ( preferencesPath ) ; const json = JSON . parse ( jsonBuf . toString ( 'utf-8' ) ) ; json . profile . exit_type = 'Normal' ; await fs . writeFile ( preferencesPath , JSON . stringify ( json ) ) ; } ; const hasNotLoggedIn = async ( pid ) : Promise < boolean > => { return pid2title ( pid ) . then ( ( title ) => ! title . includes ( '- YouTube Studio' ) ) . catch ( ( e ) => { console . error ( e ) ; return true ; } ) ; } ; const runUncontrolledChrome = async ( userDataDir ) : Promise < void > => { const chromeProcess = spawn ( chromeLocation , [ 'https://studio.youtube.com' , '--no-first-run' , '--no-default-browser-check' , '--disable-translate' , '--disable-default-apps' , '--disable-popup-blocking' , '--disable-zero-browsers-open-for-tests' , `--user-data-dir=${ userDataDir }` , ] ) ; const pid = chromeProcess . pid ; do { await delayAsync ( 1000 ) ; } while ( isRunning ( pid ) && ( await hasNotLoggedIn ( pid ) ) ) ; chromeProcess . kill ( ) ; await delayAsync ( 1000 ) ; await chromePreventCrashDialog ( userDataDir ) ; } ; const makeLoggedInChromeProfile = async ( ) : Promise < ITempDirectory > => { const modulePrefix = 'node-apiless-youtube-upload-' ; const tempDir = await createTmpDir ( modulePrefix ) ; const { base : tmpBase , dir } = Path . parse ( tempDir . path ) ; for ( const file of await fs . readdir ( dir ) ) { if ( file === tmpBase || ! file . startsWith ( modulePrefix ) ) continue ; const prevProfilePath = Path . join ( dir , file ) ; console . log ( 'Removing temp profile from previous run' , prevProfilePath ) ; await fs . rm ( prevProfilePath , { recursive : true , force : true , maxRetries : 3 } ) ; } return runUncontrolledChrome ( tempDir . path ) . then ( ( ) => tempDir ) . catch ( ( err ) => tempDir . remove ( ) . then ( ( ) => Promise . reject ( err ) ) ) ; } ; const fetchCookies = async ( driver ) : Promise < Cookies > => { await driver . get ( URL . LOADER ) ; await driver . sleep ( 4000 ) ; await driver . get ( URL . GOOGLE ) ; await driver . get ( URL . YOUTUBE_STUDIO ) ; const currentUrl = await driver . getCurrentUrl ( ) ; const isLoggedIn = [ URL . YOUTUBE , URL . YOUTUBE_STUDIO ] . some ( ( link ) => currentUrl . startsWith ( link ) ) ; if ( ! isLoggedIn ) { throw new Error ( 'The login session could not be loaded (either user never logged in, ' + "random lag or google account doesn't have a youtube attached)" ) ; } await driver . get ( URL . SELECT_ACCOUNT_YOUTUBE ) ; await driver . wait ( until . urlIs ( URL . YOUTUBE ) , 60 * 1000 ) ; const webDriverCookies = await driver . manage ( ) . getCookies ( ) ; return new Cookies ( webDriverCookies ) ; } ; export default async ( ) : Promise < Cookies > => { let profilePath ; let webDriver ; try { profilePath = await makeLoggedInChromeProfile ( ) ; webDriver = await makeWebDriver ( { automation : true , userDataDir : profilePath . path } ) ; return await fetchCookies ( webDriver ) ; } finally { if ( webDriver ) await webDriver . quit ( ) ; if ( profilePath ) await profilePath . remove ( ) ; } } ;