import {until, By, WebElement} from 'selenium-webdriver' import fs from 'fs' import {Cookies, URL, makeWebDriver} from '../helpers' import {VideoObj} from './uploadVideo' function getSubstr(str:string, narrow:boolean, len:number){ if(!narrow){ return str.substr(0, len); } else{ var ret = ''; for(var i = 0; i < str.length; i++){ if(str.substr(0, i+1).replace(/[\u4e00-\u9fa5]/g, '**').length<=len){ ret = str.substr(0, i+1); } else{ break; } } return ret; } } const validateVideoObj = (videoObj : VideoObj) => { if (!videoObj.videoPath) throw new Error("VideoObj: missing required property videoPath") if (!videoObj.title) throw new Error("VideoObj: missing required property title") var titleLen = videoObj.title.replace(/[\u4e00-\u9fa5]/g, '**').length; if (titleLen < 16) throw new Error("VideoObj: given title is shorter than min 16 characters"); if (titleLen > 40){ console.log("VideoObj: given title is longer than max 40 characters"); videoObj.title = getSubstr(videoObj.title, true, 40); } if (videoObj.description.length > 100){ console.log("VideoObj: given description is longer than max 100 characters"); videoObj.description = getSubstr(videoObj.description, false, 100); } if (!fs.existsSync(videoObj.videoPath)) throw new Error(`VideoObj: given videoPath doesn't exist on disk (${videoObj.videoPath})`) if (videoObj.thumbnailPath && !fs.existsSync(videoObj.thumbnailPath)) throw new Error(`VideoObj: given thumbnailPath doesn't exist on disk (${videoObj.thumbnailPath})`) } export default async (videoObj : VideoObj, cookies : Cookies, headlessMode = true, onProgress = console.log):Promise => { if (!cookies || !cookies.length) throw new Error("Can't upload video: cookies not set.") validateVideoObj(videoObj) // Fill default values videoObj = { description: '', category:'无', ...videoObj } const driver = await makeWebDriver({headless: headlessMode, fullsize: true}) const enterEmojiString = async (webElement : WebElement, string : 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 findElements = async (cssSelector : string) => { var webEls = await driver.findElements(By.css(cssSelector)) if (webEls[0]) await driver.executeScript('arguments[0].scrollIntoViewIfNeeded()', webEls[0]) return webEls } const findElement = async (cssSelector : string) => { 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 : string) => { var els = await findElements(cssSelector) if (els.length == 0) return false return els[0] } try { onProgress('Settings cookies..') // Load haokan page to set up cookies await driver.get(URL.HAOKAN) // Add cookies for (let cookie of cookies) await driver.manage().addCookie(cookie) await driver.sleep(1000) //refresh await driver.get(URL.HAOKAN) await driver.sleep(1000) onProgress('redirect to upload page..') await (await findElement('.icon-upload')).click() var windows = await driver.getAllWindowHandles(); if(windows.length==2){ await driver.switchTo().window(windows[1]); } // Wait for stuff to fully load await driver.sleep(1000*5) // Check whether user is still in login state (which is the case if cookies are not valid / are expired) if (!(await driver.getCurrentUrl()).includes(URL.HAOKAN_UPLOAD)) { throw new Error(`Cookies are expired or not valid.`) } // Wait for file input to appear await driver.wait(until.elementsLocated(By.css("input[type=file]")), 1000*10) onProgress('Initializing video..'); // Enter file path await (await findElement("input[type=file]")).sendKeys(videoObj.videoPath) // Wait for file metadata to upload await driver.wait(until.elementsLocated(By.css(".text-success")), 1000*60*5) var uploadVideoSucText = await findElement('.text-success'); if(uploadVideoSucText){ // Wait for random javascript garbage to load await driver.sleep(1000*2); onProgress('Initializing title and description..') // Enter title and description var editBoxes = await findElements('.hk-input'); var titleBox = editBoxes[0]; var descriptionBox = editBoxes[1]; await enterEmojiString(titleBox, videoObj.title); await enterEmojiString(descriptionBox, videoObj.description); await driver.sleep(1000); onProgress('Entering custom thumbnail..') async function onUploadThumbnail(){ await (await findElement('.btn-publish')).click(); await driver.sleep(1000*10); if(!(await tryFindElement('.btn-publish'))){ await (await findElement('p.product')).click(); await driver.sleep(1000 * 5); if (await tryFindElement('.v-title') && (await (await findElement('.v-title')).getText()) == videoObj.title) { await (await findElement('.v-title')).click(); var windows = await driver.getAllWindowHandles(); if (windows.length == 3) { await driver.switchTo().window(windows[2]); await driver.sleep(1000 * 5); return JSON.stringify({ status: 1, videoUrl: await driver.getCurrentUrl() }); } else{ console.error('Fail to publish because could not get url!'); return JSON.stringify({ status: 1, videoUrl: URL.HAOKAN_PRODUCT }); } } else { console.error('Fail to publish because could not open product page!'); return JSON.stringify({ status: 1, videoUrl: URL.HAOKAN_PRODUCT }); } } else{ throw new Error(`Fail to publish because publish button doesn't disappear!`); } } // Enter custom thumbnail if (videoObj.thumbnailPath) { await (await findElement(".click-area")).click(); await driver.sleep(1000); await (await findElements(".modal-header-tab"))[1].click(); var thumbnailBox = await findElement('.image-uploader-input'); await thumbnailBox.sendKeys(videoObj.thumbnailPath); await driver.wait(until.elementsLocated(By.css(".btn-submit")), 1000*10) var thumbFinBtn = await findElement('.btn-submit'); if(thumbFinBtn){ thumbFinBtn.click(); await driver.sleep(1000*2); return await onUploadThumbnail(); } else{ throw new Error('Fail to upload thumbnail!'); } } else{ return await onUploadThumbnail(); } } else{ throw new Error('Fail to upload video!'); } } catch(e){ return Promise.resolve(e); } finally { await driver.quit(); } }