// ==UserScript== // @name 抖音视频文案 // @namespace https://bbs.tampermonkey.net.cn/ // @version 0.1.0 // @description try to take over the world! // @author You // @match https://yuntu.oceanengine.com/yuntu_ng/content/creative/content_lab* // @icon https://www.google.com/s2/favicons?domain=oceanengine.com // @require https://cdn.jsdelivr.net/npm/sweetalert2@11.10.2/dist/sweetalert2.all.min.js // @grant GM_xmlhttpRequest // @grant GM_download // ==/UserScript== (function () { 'use strict'; var newButton = document.createElement("button") newButton.innerHTML = "视频文案下载" newButton.style.height = "34px" newButton.style.lineHeight = "34px" newButton.style.align = "center" newButton.style.color = "white" newButton.style.background = "#1f4bd9" newButton.style.border = "1px solid #1f4bd9" newButton.style.borderRadius = "3px" newButton.style.marginLeft = '10px' newButton.style.fontSize = '12px' newButton.style.padding = '0 10px' newButton.type = "button" newButton.addEventListener("click", handleButtonClick) //监听按钮点击事件 function appendDoc() { setTimeout(() => { var a = document.querySelector(".headerContainer-rEnBw7 > .tabContainer-vjnZry") if (a) { a.append(newButton) } appendDoc() }, 1000) } appendDoc() // const res = await fetch("https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=7284174158659931392", { // "headers": { // "accept": "application/json, text/plain, */*" // }, // "body": null, // "method": "GET" // }) // .then(res => res.json()) // .then(console.log) function loadScript(url) { return new Promise((resolve, reject) => { let script = document.createElement('script') script.src = url script.type = 'text/javascript' script.onload = resolve script.onerror = reject document.head.appendChild(script) }) } //跨域请求函数 function request(id) { return new Promise(resolve => { GM_xmlhttpRequest({ method: "GET", url: "https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=" + id, onload: function (res) { if (res.status == 200) { var text = res.responseText var json = JSON.parse(text) console.log(json) var uri = json.aweme_detail.video.play_addr.uri //uri的位置 resolve([id, uri]) } else { resolve(null) } } }); }); } //通用fetch函数 async function fetchData(url, method, body = null) { const headers = { "content-type": "application/json" } const options = { method: method, headers: headers } if (body) { options.body = JSON.stringify(body) } const response = await fetch(url, options) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } else { const json = await response.json() return json } } class chainFunction { async getID() { //从url中获取aadvid const url = window.location.href const aadvid = url.split('aadvid=')[1] this.aadvid = aadvid return this } async selectType() { const inputOptions = new Promise((resolve) => { resolve({ "uri": "后台视频uri", "id": "前台视频id" }) }) const { value: type } = await Swal.fire({ title: "选择视频id类型", input: "radio", footer: '视频id和视频uri分别是什么?', inputOptions, inputValidator: (value) => { if (!value) { return "必选" } } }) console.log(type) return type } async inputVideoId(type) { const { value: videoId } = await Swal.fire({ input: "textarea", title: "视频" + type + "列表", currentProgressStep: 0, inputPlaceholder: "请输入需要解析的视频列表, 每行一个", inputAttributes: { "aria-label": "请输入需要解析的视频列表, 每行一个" }, showCancelButton: true }) const idList = videoId.split('\n') console.log(idList) //this.idList = idList return idList } async msg() { const Toast = Swal.mixin({ toast: true, position: "top-end", showConfirmButton: false, timer: 5000, }) Toast.fire({ icon: "success", title: "任务开始,如提示脚本正在请求跨源资源,请勾选始终允许" }) } async getVideoUri(idList) { let arr = [] for (let i = 0; i < idList.length; i++) { let result = await request(idList[i]) if (result != null) arr.push(result) } // this.uriArr = uriArr return arr } async getVideoText(arr) { let url = "https://yuntu.oceanengine.com/yuntu_ng/api/v1/GetContentFormulaAndScript?aadvid=" + this.aadvid let promises = arr.map(async (uri, index) => { let body = { "vid": uri[1] } let res = await fetchData(url, "POST", body) let videoText = res.data.ori_script.text arr[index].push(videoText) }) await Promise.all(promises) // console.log(uriArr) return arr } async downloadCsv(arr) { const headers = ['视频id', '视频uri', '视频文案']; let csv = headers.join('|') + '\n' arr.forEach(row => { if (row[0] !== "") row[0] = "'" + row[0] // 给视频id添加一个前缀' 避免被科学计数 csv += row.join('|') + '\n' }) var hiddenElement = document.createElement('a') //var fileName = this.crowdNameList.map(name => name.length > 5 ? name.slice(0, 10) + '..' : name).join('_') hiddenElement.href = 'data:text/csv;charset=utf-8,%EF%BB%BF' + encodeURI(csv) hiddenElement.target = '_blank' hiddenElement.download = '抖音视频文案' + ".csv" hiddenElement.click() } } // 使用链式函数 async function handleButtonClick() { await loadScript('https://cdn.jsdelivr.net/npm/sweetalert2@11.10.2/dist/sweetalert2.all.min.js') //console.log("Swal loaded successfully") let chain = new chainFunction() await chain.getID() let type = await chain.selectType() if (type == "id") { let idList = await chain.inputVideoId(type) await chain.msg() let uriArr = await chain.getVideoUri(idList) let textArr = await chain.getVideoText(uriArr) await chain.downloadCsv(textArr) } if (type == "uri") { let idList = await chain.inputVideoId(type) await chain.msg() let uriArr = idList.map((item, index) => ["", item]) //如果输入的不是视频id,数组第一个值赋0 let textArr = await chain.getVideoText(uriArr) await chain.downloadCsv(textArr) } } })()