UNPKG

node-apiless-youtube-upload-nc

Version:

Upload videos to Youtube in Node.js without any Youtube API dependency by using Selenium.

203 lines (202 loc) 9.38 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const selenium_webdriver_1 = require("selenium-webdriver"); const helpers_1 = require("../helpers"); var URL = require('url'); const GOOGLE_URL = `https://google.com`; const YOUTUBE_STUDIO_URL = `https://studio.youtube.com`; exports.default = async (url, hs, cookies, vid, headlessMode = true, onProgress = console.log) => { if (!url) throw new Error("url not set."); if (!hs) throw new Error("hs not set."); if (!cookies || !cookies.length) throw new Error("Can't check video copyright: cookies not set."); const driver = await (0, helpers_1.makeWebDriver)({ headless: headlessMode, fullsize: true }); const enterEmojiString = async (webElement, string) => { // sendKeys(string) doesn't support emojis (causes a crash) // youtube custom input elements don't have "value" property (but webEl.clear() still works) // clipboard hack works but not in headless mode // also editing innerHTML causes racing conditions with the underlying javascript mechanism // solution is to use an obsolete method document.execCommand('insreText') await driver.sleep(500); webElement.click(); await driver.sleep(250); webElement.clear(); await driver.sleep(250); await driver.executeScript(` arguments[0].focus(); document.execCommand('insertText', false, arguments[1]); `, webElement, string); }; const ensureNoSecurityWarning = async () => { await driver.executeScript("if (document.querySelector('ytcp-auth-confirmation-dialog')) document.querySelector('ytcp-auth-confirmation-dialog').remove()"); }; const findElements = async (cssSelector) => { var webEls = await driver.findElements(selenium_webdriver_1.By.css(cssSelector)); if (webEls[0]) await driver.executeScript('arguments[0].scrollIntoViewIfNeeded()', webEls[0]); return webEls; }; const findElement = async (cssSelector) => { var els = await findElements(cssSelector); if (els.length === 0) throw new Error(`Element was not found with selector '${cssSelector}'`); return els[0]; }; const tryFindElement = async (cssSelector) => { var els = await findElements(cssSelector); if (els.length == 0) return false; return els[0]; }; function getRandom(max, min) { if (min !== undefined && max != min) { return Math.floor(min + (max - min) * Math.random()); } else { return Math.floor(max * Math.random()); } } var securityIgnoreInterval = null; try { onProgress('Settings cookies..'); // Load google page to set up cookies await driver.get(GOOGLE_URL); // Add cookies for (let cookie of cookies) await driver.manage().addCookie(cookie); onProgress('Opening Youtube Studio..'); // Open Youtube Studio page await driver.get(YOUTUBE_STUDIO_URL); // Wait for stuff to fully load await driver.sleep(1000); securityIgnoreInterval = setInterval(() => { ensureNoSecurityWarning(); }, 500); // Check if url is still studio.youtube.com and not accounts.google.com (which is the case if cookies are not valid / are expired) var currentUrl = (await driver.getCurrentUrl()); if (!currentUrl.includes('studio.youtube.com/')) { throw new Error(`Cookies are expired or not valid. (tried to upload, was redirected to ${currentUrl}`); } // Open url if (!url.endsWith('/videos')) { url += '/videos'; } onProgress('Opening ' + url); await driver.get(url); await driver.sleep(1000); var items = (await findElements('#items ytd-grid-video-renderer a[id=thumbnail]')); var vids = []; for (var i = 0; i < items.length; i++) { var params = URL.parse(await items[i].getAttribute('href')).search.substr(1).split('&'); for (var j = 0; j < params.length; j++) { var pair = params[j].split('='); if (pair[0] == 'v') { vids.push(pair[1]); break; } } } if (vids.length) { var sn = -1; for (var i = 0; i < vids.length; i++) { if (vids[i] == vid) { sn = i; break; } } if (sn == 0) { console.log(`latest video has been remarked!`); return Promise.resolve(vid); } else { vid = vids[0]; if (sn != -1) { vid = vids[sn - 1]; } if (Math.random() < 0.9) { var url = 'https://www.youtube.com/watch?v=' + vid; onProgress('Opening ' + url); await driver.get(url); await driver.sleep(getRandom(5, 2) * 1000); var metaEl = await findElement('[id=primary-inner] [id=meta]'); var location = await metaEl.getRect(); onProgress('click like button..'); var likeCnt = getRandom(3, 1); for (var i = 0; i < likeCnt; i++) { await driver.executeScript('window.scrollTo(0, arguments[0])', location.y + location.height + 300 * i); await driver.sleep(getRandom(5, 2) * 1000); var likeBtns = await findElements('ytd-toggle-button-renderer[id=like-button]'); if (likeBtns.length) { var likeBtn = likeBtns[getRandom(likeBtns.length, 0)]; await driver.executeScript('arguments[0].scrollIntoViewIfNeeded()', likeBtn); if ((await likeBtn.getAttribute('class')).indexOf('style-default-active') == -1) { try { await likeBtn.click(); } catch (e) { console.error('error occur when click like button!'); console.error(e); } } } await driver.sleep(getRandom(2, 1) * 1000); await driver.executeScript('window.scrollTo(0, 0)'); await driver.sleep(getRandom(2, 1) * 1000); } onProgress('add comment..'); await driver.executeScript('window.scrollTo(0, arguments[0])', location.y + location.height); await driver.sleep(getRandom(5, 2) * 1000); onProgress('remark..'); (await findElement('#simplebox-placeholder')).click(); await driver.sleep(1000); var cts = await findElements('yt-formatted-string[id=content-text]'); if (cts.length) { var ct = cts[getRandom(cts.length, 0)]; await driver.executeScript('arguments[0].scrollIntoViewIfNeeded()', ct); try { var curText = await ct.getText(); var rVal = Math.random(); if (rVal < 0.15) { hs = curText + ' ' + hs; } else if (rVal < 0.3) { hs = hs + ' ' + curText; } else if (rVal < 0.4) { hs = curText; } } catch (e) { console.error('error occur when get text!'); console.error(e); } } var inputElem = await findElement('[id=contenteditable-root]'); await driver.executeScript('arguments[0].scrollIntoViewIfNeeded()', inputElem); await inputElem.sendKeys(hs); await driver.sleep(getRandom(5, 2) * 1000); (await findElement('#submit-button [id=button]')).click(); await driver.sleep(getRandom(5, 2) * 1000); } else { console.log('ignore comments!'); } return Promise.resolve(vid); } } else { console.log(`videos is empty!`); return Promise.resolve(''); } } catch (e) { return Promise.resolve(''); } finally { if (securityIgnoreInterval) clearInterval(securityIgnoreInterval); await driver.quit(); } };