<template>
	<div>
		<el-container style="height: 100vh;">
			<el-header height="45px" style="background-color: #fafafa; padding: 0 10px;">
				<soft-header ref="headerRef" @update-soft="updateSoft()" @login-url="loginUrl" @clear-cache="clearCache"></soft-header>
			</el-header>
							
			<el-main ref="el-main" style="background-color: #fafafa;">
				<template>
					<div style="padding: 10px 20px 0 20px; height: 100%;">
							<div style="text-align: center;">
								<p class="c-titles">星优-在线视频下载助手</p>
								<div style="padding-top: 8px; font-weight: 600;">
									支持
									<img src="../assets/image/douyin.png" class="soft-icon" title="抖音" />
									<img src="../assets/image/weibo.png" class="soft-icon" title="微博" />
									<img src="../assets/image/kuaishou.png" class="soft-icon" title="快手" />
									<img src="../assets/image/xiaohongshu.png" class="soft-icon" title="小红书" />
									<img src="../assets/image/haokan.png" class="soft-icon" title="好看视频" />
									<img src="../assets/image/bilibili.png" class="soft-icon" title="b站" />
									<img src="../assets/image/cctv.png" class="soft-icon" title="cctv" />
									<img src="../assets/image/56.png" class="soft-icon" title="56视频" />
									<img src="../assets/image/acfun.png" class="soft-icon" title="acfun" />
									等多个平台，<span style="color: #F56C6C;">（请使用win7以上系统）</span>
									
									<el-popover placement="bottom" popper-class="popper-open" trigger="hover" content="会员/付费/版权视频不支持下载">
										<i class="el-icon-info" slot="reference" style="margin-right: 10px; color: #F56C6C;"></i>
									</el-popover>
								</div>
							</div>
						
						<div style="margin-top: 20px;">
							<el-input type="textarea" :rows="5" placeholder="请输入需要解析的视频地址" v-model="formatUrl"></el-input>
							
							<div class="content-top" style="padding: 10px 0; text-align: center; display: inherit;">
								<el-button type="danger" @click="startParsing()" :loading="parseLoading">开始解析</el-button>
							</div>
						</div>
					
						<div class="table-scroll" style="height: calc(100% - 290px); overflow: hidden;">
							<el-row type="flex" justify="space-between">
								<div>
									<h4 style="display: inline-block;">
										视频信息：
									</h4>
									
									<el-button @click="mergeClick()" size="small" :disabled="bUrl" title="解析B站网址后启用">B站音视频合并</el-button>
								</div>
								<el-row type="flex" style="align-items: center;">
									<div class="set-item">
										<span class="set-title">视频保存目录：</span>
										<el-input :title="downloadDir" ref="upload-input" size="mini" @focus="pickPath" placeholder="请选择输出目录" v-model="downloadDir" readonly style="width:200px;" prefix-icon="el-icon-folder"></el-input>
										<el-popover placement="bottom" popper-class="popper-open" trigger="hover" content="打开保存目录">
											<i class="el-icon-folder-opened" slot="reference" style="padding-left: 5px; cursor: pointer; font-size: 22px; vertical-align: middle;" @click="openFolder()"></i>
										</el-popover>
									</div>
								</el-row>
							</el-row>
							<vxe-table ref="xTable" show-overflow class="img-table" max-height="100%" empty-text="没有更多数据了！" :loading="tabLoading" :row-config="{isHover: true}" 
								:loading-config="{icon: 'vxe-icon-indicator roll', text: '加载中...'}" :data="videoList" :scroll-y="{enabled: true}">
								<vxe-column field="title" title="标题">
									<template #default="{ row }">
										{{row.title}}[{{row.format_id}}]
									</template>
								</vxe-column>
								<vxe-column field="ext" title="扩展名" width="70"></vxe-column>
								<vxe-column field="resolution" title="分辨率" width="100"></vxe-column>
								<vxe-column field="fps" title="帧率" width="60"></vxe-column>
								<vxe-column field="filesize" title="大小" width="90">
									<template #default="{ row }">
										<span v-if="row.filesize">{{$utils.handleSize(row.filesize)}}</span>
										<span v-else-if="row.filesize_approx">{{$utils.handleSize(row.filesize_approx)}}</span>
									</template>
								</vxe-column>
								<vxe-column field="vcodec" title="视频编码" width="120"></vxe-column>
								<vxe-column field="acodec" title="音频编码" width="120"></vxe-column>
								<vxe-column field="status" title="状态" width="140">
									<template #default="{ row }">
										<template v-if="row.status == '1'">
											<i class="el-icon-info" style="font-size: 16px; color: #999;"></i>
											<span>待操作</span>
										</template>
										<template v-if="row.status == '2'">
											<i class="el-icon-loading" style="font-size: 16px; color: #999;"></i>
											<span>下载中.. <span v-if="row.percent">{{row.percent}}%</span></span>
										</template>
										<template v-if="row.status == '3'">
											<i class="el-icon-success" style="font-size: 16px; color: #19be6b;"></i>
											<span>下载完成</span>
										</template>
										<template v-if="row.status == '4'">
											<i class="el-icon-error" style="font-size: 16px; color: #ed4014;"></i>
											<span>下载出错,请重试!</span>
										</template>
									</template>
								</vxe-column>
								<vxe-column title="操作" width="100">
									<template #default="{ row, rowIndex }">
										<!-- <el-button v-if="row.urlList && row.urlList.length > 0" type="danger" size="mini" plain @click="copy(rowIndex)">复制</el-button> -->
										<el-button :loading="row.loading" type="danger" size="mini" plain @click="downloadVideo(rowIndex)">下载</el-button>
									</template>
								</vxe-column>
							</vxe-table>
						</div>
					</div>
				</template>
				
				<!-- B站音视频合并提示框 -->
				<el-dialog title="B站音视频合并" :visible.sync="mergeModal" width="420px" :close-on-click-modal="false" :close-on-press-escape="false">
					<div class="member-model">
						<div class="handle-item">
							<label class="handle-label">视频文件：</label>
							<el-upload :style="{display: 'inline-block'}" action="/" :before-upload="getVideoPath">
								<el-input type="text" readonly  size="small" v-model="bVideoInfo.videoPath" style="width:220px;" placeholder="点击选择文件"></el-input>
								<i class="el-icon-folder-opened open-folder" style="font-size:22px;"></i>
							</el-upload>
						</div>
						<div class="handle-item" v-if="bVideoInfo.videoDuration">
							<label class="handle-label">视频时长：</label>
							<span style="color: #999;">{{formatSeconds(bVideoInfo.videoDuration)}}</span>
						</div>
						<div class="handle-item">
							<label class="handle-label">音频文件：</label>
							<el-upload :style="{display: 'inline-block'}" action="/" :before-upload="getAudioPath">
								<el-input type="text" readonly size="small" v-model="bVideoInfo.audioPath" style="width:220px;" placeholder="点击选择文件"></el-input>
								<i class="el-icon-folder-opened open-folder" style="font-size:22px;"></i>
							</el-upload>
						</div>
						<div class="handle-item" v-if="bVideoInfo.audioDuration">
							<label class="handle-label">音频时长：</label>
							<span style="color: #999;">{{formatSeconds(bVideoInfo.audioDuration)}}</span>
						</div>
						<div class="handle-item">
							<label class="handle-label">文件名称：</label>
							<el-input type="text" size="small" placeholder="请输入文字水印内容" style="width:220px;" v-model="bVideoInfo.title"></el-input>
						</div>
						<div class="handle-item">
							<label class="handle-label">文件类型：</label>
							<el-select size="small" style="width:220px;" v-model="bVideoInfo.suffix">
								<el-option value="mp4" label="mp4"></el-option>
								<el-option value="mkv" label="mkv"></el-option>
							</el-select>
						</div>
						<div class="member-btn">
							<el-button size="small" type="danger" @click="mergeVideo()" :loading="mergeLoading">开始合并</el-button>
						</div>
					</div>
				</el-dialog>

				<!-- 非会员转换提示框 -->
				<el-dialog title="非会员提示" :visible.sync="tipsModal" width="400px">
					<div class="member-model">
						<div class="tips-flex">
							<i class="el-icon-s-opportunity"></i>
							<p class="m-title">{{tipsDesc}}</p>
						</div>
						<div class="member-btn">
							<el-button size="small" round type="primary" @click="openVip()">开通会员</el-button>
						</div>
					</div>
				</el-dialog>
				
				<!-- 抖音下载 -->
				<el-dialog title="视频下载" :visible.sync="dyModal" width="300px" :close-on-click-modal="false" :close-on-press-escape="false">
					<el-divider content-position="left" v-if="commonUrl">
						浏览器下载
						<el-popover placement="bottom" popper-class="popper-open" trigger="hover" content="点击复制地址到浏览器中打开">
							<i class="el-icon-info" slot="reference" style="margin-left: 10px; color: #F56C6C;"></i>
						</el-popover>
					</el-divider>
					<div style="text-align: center;">
						<el-button v-if="commonUrl" size="small" round type="primary" @click="copyText(commonUrl)">复制下载地址</el-button>
					</div>
					
					<el-divider content-position="left">
						迅雷下载
						<el-popover placement="bottom" popper-class="popper-open" trigger="hover" content="点击复制下载地址到迅雷中下载">
							<i class="el-icon-info" slot="reference" style="margin-left: 10px; color: #F56C6C;"></i>
						</el-popover>
					</el-divider>
					<div style="text-align: center;">
						<div v-for="(item, index) in thunderUrl" :key="index">
							<el-button size="small" round type="primary" @click="copyText(item)" style="margin-bottom: 15px;">复制下载地址{{index+1}}</el-button>
							</br>
						</div>
					</div>
					
					<el-divider content-position="left">
						普通下载
						<el-popover placement="bottom" popper-class="popper-open" trigger="hover" content="使用软件下载可能会失败,建议使用浏览器/迅雷下载">
							<i class="el-icon-info" slot="reference" style="margin-left: 10px; color: #F56C6C;"></i>
						</el-popover>
					</el-divider>
					<div style="text-align: center;">
						<el-button v-if="selectIndex > -1" :loading="videoList[selectIndex].loading" size="small" round type="primary" @click="downloadVideo(selectIndex, true)" style="margin-bottom: 15px;">点击下载</el-button>
					</div>
				</el-dialog>
		
			</el-main>
			
			<el-footer height="48px">
				<!-- 更新 -->
				<soft-update ref="updateRef" :showDowload="dowloadModel" :dowloadFinish="finishModel"></soft-update>
			</el-footer>
		</el-container>
	</div>
</template>

<script>
	import os from 'os'
	import fs from 'fs'
	import request from 'request'
	import path from 'path';
	import xlsx from 'node-xlsx';
	import softUpdate from './update.vue';
	import softHeader from './header.vue';
	import electronApi from '@/utils/electronApi';
	import pjson from '/package.json'
	// import puppeteer from 'puppeteer'
	import puppeteer from 'puppeteer-extra'
	const axios = require('axios');
	const StealthPlugin = require('puppeteer-extra-plugin-stealth');
	const listNameArr = ['douyin','kuaishou','weibo','','','','','', '' ,'common'];
	import { VxeTooltip } from 'vxe-table';
	
	let separator = '';
	if (os.platform == 'linux') {
		separator = '/'
	} else {
		separator = '\\'
	}
	export default {
		name: 'landing-page',
		components: {
			softUpdate,
			softHeader
		},
		data() {
			return {
				selectIndex: -1,
				usageTimes: 3,
				tipsModal: false,
				tipsDesc: "暂无下载权限，请开通VIP会员使用",
				mergeModal: false,
				bVideoInfo: {
					videoPath: '',
					audioPath: '',
					title: '',
					suffix: 'mp4',
					videoDuration: '',
					audioDuration: '',
				},

				formatUrl: '',
				downloadUrl: '', //格式化之后的dtp下载地址
				tabLoading: false,
				videoInfo: {},
				videoList: [],
				productName: pjson.softInfo.softName,
				imgUrl: this.$api.imgUrl,
				imgSrc: '',

				fileList: [],
				
				downloadDir: os.userInfo().homedir + separator + "Downloads",
				dowloadModel: false,
				finishModel: false,

				loading: false,
				/** 浏览器名称 **/
				videoBrowser: null,
				loginBrowser: null, // 登录用的浏览器实例
				parseLoading: false,
				mergeLoading: false,
				bUrl: true,
				
				dyModal: false,
				dyIndex: 0,
				thunderUrl: [],
				commonUrl: '',
				
			};
		},
		async mounted() {
			this.$refs.updateRef.updateSoft(true);

			let homedir = os.userInfo().homedir;
			if (fs.existsSync(homedir + separator + "Desktop")) {
				this.downloadDir = homedir + separator + "Desktop"
			} else {
				this.downloadDir = homedir + separator + "Downloads"
			}
			
			if (!fs.existsSync(os.tmpdir() + separator + 'chrome-data-capture-video')) {
				fs.mkdirSync(os.tmpdir() + separator + 'chrome-data-capture-video');
			}

			// 打开浏览器
			const {
				shell
			} = require('electron');
			const links = document.querySelectorAll('a[href]');
			links.forEach(link => {
				link.addEventListener('click', e => {
					const url = link.getAttribute('href');
					e.preventDefault();
					shell.openExternal(url);
				});
			});
			
			// 初始化开发者设置
			this.$utils.setStorage('headless', 1);
			this.$utils.setStorage('waitUntil', 'networkidle2');
			
		},
		methods: {
			// 实时获取开发者设置
			initDevelop(){
				let develop = {
					headless: true,
					waitUntil: 'networkidle2'
				};
				let n1 = this.$utils.getStorage('headless');
				let n2 = this.$utils.getStorage('waitUntil');
				if(n1){
					if(n1 == 1){
						develop.headless = true;
					}else if(n1 == 2){
						develop.headless = false;
					}
				}
				if(n2){
					develop.waitUntil = n2;
				}
				
				return develop;
			},
			// 实时获取浏览器路径
			initPath(){
				let chromePath = puppeteer.executablePath().replace('win32-1', 'win64-1');
				return chromePath;
			},
			checkAuthority(){
				let authority = this.$refs.headerRef.authority;
				this.$refs.imgRef.authority = authority;
			},
			// 选择目录
			pickPath() {
				this.$refs['upload-input'].blur();
				electronApi.call('pickDir', []).then((path) => {
					if (path) {
						this.downloadDir = path;
					}
				});
			},
			// 打开自定义下载目录
			openFolder() {
				let path = this.downloadDir;
				if (fs.existsSync(this.downloadDir + separator + pjson.softInfo.softName)) {
					path = this.downloadDir + separator + pjson.softInfo.softName;
				} else {
					fs.mkdirSync(this.downloadDir + separator + pjson.softInfo.softName);
					path = this.downloadDir + separator + pjson.softInfo.softName;
				}
				electronApi.call('showItemInfolder', [path + '\\tty.tty'])
			},
			openVip() {
				this.tipsModal = false;
				this.$refs.headerRef.openVip();
			},
			updateSoft() {
				this.$refs.updateRef.updateSoft();
			},
			// 清除缓存的后续操作
			async clearCache(){
				if(this.loginBrowser){
					await this.loginBrowser.close();
					this.loginBrowser = null;
				}
				
				if(this.videoBrowser){
					await this.videoBrowser.close();
					this.videoBrowser = null;
				}
			},
			
			// 去登录
			async loginUrl(url){
				if(this.loginBrowser){
					await this.loginBrowser.close();
					this.loginBrowser = null;
				}
				if(this.videoBrowser){
					await this.videoBrowser.close();
					this.videoBrowser = null;
				}
				(async () => {
					try{
						if (!fs.existsSync(os.tmpdir() + separator + 'chrome-data-capture-video')) {
							fs.mkdirSync(os.tmpdir() + separator + 'chrome-data-capture-video');
						}
						let userDataDir = os.tmpdir() + separator + 'chrome-data-capture-video';
						puppeteer.use(StealthPlugin());
						this.loginBrowser = await puppeteer.launch({
							headless: false,
							executablePath: this.initPath(),
							args: ['--window-size=1280,800'],
							userDataDir: userDataDir, 
						});
						const page = await this.loginBrowser.newPage();
						await page.setViewport({ width: 1280, height: 800 });
						
						await page.evaluateOnNewDocument(() => {
						    const newProto = navigator.__proto__;
							delete newProto.webdriver;
							navigator.__proto__ = newProto;
						});
						await page.goto(url, {waitUntil : 'networkidle2'});
					}catch(e){
						this.showError(e);
					}
				})();
			},
			getVideoPath(e){
				if (e) {
					this.bVideoInfo.videoPath = e.path;
					
					electronApi.spawnExec(['ffprobe.exe','-i', e.path,'-v','quiet','-print_format','json','-show_format', '-show_streams', '-hide_banner']).then((result) => {
						result = result.stdout.toString();
						result = JSON.parse(result);
						this.bVideoInfo.videoDuration = result.format.duration;
					});
					
				}
			},
			getAudioPath(e){
				if (e) {
					this.bVideoInfo.audioPath = e.path;
					
					electronApi.spawnExec(['ffprobe.exe','-i', e.path,'-v','quiet','-print_format','json','-show_format', '-show_streams', '-hide_banner']).then((result) => {
						result = result.stdout.toString();
						result = JSON.parse(result);
						this.bVideoInfo.audioDuration = result.format.duration;
					});
				}
			},
			mergeClick(){
				this.mergeModal = true;
				this.bVideoInfo.title = this.videoList[0].title;
			},
			// b站视频合并
			mergeVideo(){
				let newPath = this.downloadDir + separator + pjson.softInfo.softName;
				let params = [
					'ffmpeg.exe',
					'-i',
					this.bVideoInfo.videoPath,
					'-i',
					this.bVideoInfo.audioPath,
					'-c',
					'copy',
					newPath + separator + this.bVideoInfo.title + '.' + this.bVideoInfo.suffix,
					'-hide_banner',
					'-y'
				];
				const regexTime = /time=(.*?) bitrate/;
				this.mergeLoading = true;
				electronApi.spawnExec(params,{
					stderr:(data) =>{
						// let timeStr = regexTime.exec(data.toString());
						// if(timeStr && timeStr[1]){
						// 	let percent = Math.ceil((this.getSs(timeStr[1])/this.bVideoInfo.videoDuration).toFixed(2) * 100);
						// }
					}
				}).then(res => {
					this.mergeLoading = false;
					this.$message({message: '恭喜你，合并已完成！', type: 'success'});
					electronApi.call('showItemInfolder',[this.downloadDir + separator + pjson.softInfo.softName +'\\tty.tty']);
				}).catch(err =>{
					this.mergeLoading = false;
					this.$notify.error({
						title: '合并失败',
						message: '视频参数有误，请重试! ',
					});
				})
			},
			// 开始解析
			async startParsing(){
				if(this.formatUrl.trim()){
					let formatUrl = this.formatUrl.trim();
					
					if(formatUrl.indexOf('https://') == -1){
						this.$message.error('错了哦，请输入正确的视频地址(https://开头)');
						return false;
					}
					let arr = formatUrl.split('https://');
					formatUrl = 'https://' + arr[1];
					this.downloadUrl = formatUrl;
					
					const regex = /https:\/\/.*?.douyin.com/;
					const res = regex.exec(formatUrl);
					if(res && res.length > 0){ //抖音视频解析，使用puputter
						this.videoList = [];
						let reg2 = /[?&]modal_id=(\w+)/;
						let res2 = reg2.exec(formatUrl);
						if(res2){
							let modal_id = res2[1];
							formatUrl = res[0] + '/video/'+modal_id;
							this.downloadUrl = formatUrl;
						}
						this.douyinParsing(formatUrl);
						return false;
					}
					
					this.parseLoading = true;
					this.tabLoading = true;
					this.videoList = [];
					this.selectIndex = -1;
					let params = [
						'--dump-json',
						formatUrl
					];
					electronApi.spawnExec(['dlp.exe', ...params]).then(res => {
						this.parseLoading = false;
						this.tabLoading = false;
						let info = res.stdout ? res.stdout.toString() : '{formats: []}';
						this.videoInfo = JSON.parse(info);
						this.videoList = this.videoInfo.formats || [];
						this.videoList.map(item => {
							item.title = this.videoInfo.title,
							item.status = '1';
						})
						
						const bregex = /https:\/\/.*?.bilibili.com/;
						const bres = bregex.exec(formatUrl);
						if(bres && bres.length > 0){ //b站的视频
							this.bUrl = false;
						}else{
							this.bUrl = true;
						}
					}).catch(err =>{
						this.parseLoading = false;
						this.tabLoading = false;
						// console.log('err1',err.stderr.toString());
						let errStr = err.stderr.toString();
						this.bUrl = true;
						if(errStr.indexOf('Unsupported URL') > -1){
							this.videoParsing(formatUrl);
							return false;
						}else{
							this.$notify.error({
								title: '网址解析失败！',
								message: errStr
							});
						}
					})
				}
			},
			// 复制链接地址
			copyText(text){
				navigator.clipboard.writeText(text).then(() => {
					this.$message({message: '视频下载链接已成功复制到剪贴板', type: 'success'});
				}).catch(err => {
					this.$message.error('无法复制文本', err.toString());
				});
			},
			// 点击复制
			copy(index){
				// let authority = this.$refs.headerRef.authority.isAuthority;
				// if (!authority) {
				// 	this.tipsModal = true;
				// 	this.tipsDesc = '暂无下载权限，请开通VIP会员使用';
				// 	return false;
				// }
				
				this.commonUrl = '';
				this.thunderUrl = [];
				let tag = this.videoList[index].tag;
				let urlList = this.videoList[index].urlList;
				let title =  this.videoList[index].title;
				let text = urlList[0];
				if(urlList.length <= 0){
					this.$message.error('无法复制下载地址');
					return false;
				}
				if(tag == 'douyin'){
					this.dyModal = true;
					this.dyIndex = index;
					
					urlList.map(uitem => {
						if(uitem.startsWith('https://www.douyin.com/')){
							this.commonUrl = uitem;
						}
						if(!title){
							title = '抖音视频';
						}
						// console.log(`AA`+uitem +`&filename=`+title+`.mp4ZZ`);
						let urlBuffer = Buffer.from(`AA`+uitem +`&filename=`+title+`.mp4ZZ`).toString('base64');
						this.thunderUrl.push('thunder://'+urlBuffer);
					})
				}else{
					this.copyText(text);
				}
			},
			
			// 点击下载视频
			async downloadVideo(index, flag){
				this.selectIndex = index;
				let authority = this.$refs.headerRef.authority.isAuthority;
				if (!authority) {
					this.tipsModal = true;
					this.tipsDesc = '暂无下载权限，请开通VIP会员使用';
					return false;
				}
				this.tipsModal = false;
				
				if(this.videoList[index].urlList && this.videoList[index].urlList.length > 0 && !flag && this.videoList[index].tag == 'douyin'){ // 浏览器解析出来的下载地址
					this.copy(index);
				}else{
					let item = this.videoList[index];
					if (!fs.existsSync(this.downloadDir + separator + pjson.softInfo.softName)) {
						fs.mkdirSync(this.downloadDir + separator + pjson.softInfo.softName);
					}
					let newPath = this.downloadDir + separator + pjson.softInfo.softName;
					
					if(item.tag){ //抖音下载
						if(item.title){
							item.title = item.title.substring(0, 50);
							if(this.containsAnyChar(item.title)){  //判断是否含有特殊字符
								item.title = item.title.replace(/[\\/:*?"<>|#%&\s]/g, "");
							}
						}
						let outputPath = newPath + separator + item.title + '[' + item.format_id + ']' + '.mp4';
						let url = item.urlList.length > 0 ? item.urlList[0] : '';
						
						if(item.tag == 'douyin'){
							item.urlList.map(uitem => {
								if(uitem.startsWith('https://www.douyin.com/')){
									url = uitem;
								}
							})
						}
						
						item.status = '2';
						item.loading = true;
						await this.downloadImage(url, outputPath, item);
						item.loading = false;
						this.$forceUpdate();
						this.$message({message: '恭喜你，下载已完成！', type: 'success'});
						electronApi.call('showItemInfolder',[this.downloadDir + separator + pjson.softInfo.softName +'\\tty.tty']);
						return false;
					}else{
						// console.log(this.downloadUrl);
						let params = [
							'-f',
							item.format_id,
							'--force-overwrites',
							'-o',
							newPath + separator + '%(title).50s['+item.format_id+'].%(ext)s',
							this.downloadUrl
						];
						item.status = '2';
						item.loading = true;
						this.$forceUpdate();
						electronApi.spawnExec(['dlp.exe', ...params],{
							stdout:(data) =>{
								let str = data.toString();
								const regexDuration = /[download].*? (.*?)%/;
								const res = regexDuration.exec(str);
								if(res && res[1]){
									item.percent = res[1];
									this.$forceUpdate();
								}
							}
						}).then(res => {
							let outData = res.stdout ? res.stdout.toString() : '{}';
							item.status = '3';
							item.loading = false;
							this.$forceUpdate();
							this.$message({message: '恭喜你，下载已完成！', type: 'success'});
							electronApi.call('showItemInfolder',[this.downloadDir + separator + pjson.softInfo.softName +'\\tty.tty']);
						}).catch(err =>{
							item.status = '4';
							item.loading = false;
							this.$forceUpdate();
							// console.log(err);
						})
					}
				}

				
				
			},
			
			// 解析抖音视频
			async douyinParsing(url){
				if(this.loginBrowser){
					await this.loginBrowser.close();
					this.loginBrowser = null;
				}
				if(this.videoBrowser){
					await this.videoBrowser.close();
					this.videoBrowser = null;
				}
				if (!fs.existsSync(os.tmpdir() + separator + 'chrome-data-capture-video')) {
					fs.mkdirSync(os.tmpdir() + separator + 'chrome-data-capture-video');
				}
				
				this.parseLoading = true;
				this.tabLoading = true;
				
				setTimeout(()=> {
					this.parseLoading = false;
					this.tabLoading = false;
				}, 20000)
				
				let userDataDir = os.tmpdir() + separator + 'chrome-data-capture-video';
				// 运行不同平台的浏览器
				puppeteer.use(StealthPlugin());
				let headless = true;
				headless = this.initDevelop().headless;
				this.videoBrowser = await puppeteer.launch({
					headless: headless,
					executablePath: this.initPath(),
					userDataDir: userDataDir, 
					args: [
						'--start-maximized',
						'--no-sandbox',
						'--disable-setuid-sandbox',
						'--disable-blink-features=AutomationControlled',
					]
				});
				
				await new Promise((resolve,reject) =>{
					(async () => {
						try{
							let authority = this.$refs.headerRef.authority.isAuthority;
							const page = await this.videoBrowser.newPage();
							
							let responseVideo = [];
							let responseUrl = [];
							let responseObj = {};
							let vtitle = '视频';
							page.on('response', async(response) => {
								// 检查响应的 MIME 类型是否以 'video/' 开头
								if (response.headers()['content-type'] && response.headers()['content-type'].startsWith('video/')) {
									if(responseVideo.indexOf(response.url()) < 0 && !response.url().startsWith('blob:https://')){
										responseVideo.push(response.url());
										vtitle = await page.title();
										if(vtitle){
											vtitle = vtitle.substring(0, 50);
											if(this.containsAnyChar(vtitle)){  //判断是否含有特殊字符
												vtitle = vtitle.replace(/[\\/:*?"<>|#%&\s]/g, "");
											}
										}
									}
								}
								if (response.headers()['content-type'] && response.headers()['content-type'].startsWith('application/json')) {
									if(response.url().indexOf('/aweme/detail/') > -1){
										if(responseUrl.indexOf(response.url()) < 0){
											responseUrl.push(response.url());
										}else{
											return false;
										}
										
										let jsonText = await response.text();
										if(jsonText && typeof jsonText == 'string'){
											responseObj = JSON.parse(jsonText);
										}
									}
								}
								
							});
							let waitUntil = 'networkidle2';
							waitUntil = this.initDevelop().waitUntil;
							await page.goto(url, {waitUntil : waitUntil});
							await page.waitForTimeout(1000);
							await this.videoBrowser.close();
							
							if(responseObj['aweme_detail']){ // 返回的接口数据中有aweme_detail参数 才会解析接口数据
								let arr = ['aweme_detail']; //'video' ,'play_addr', 'url_list', '2'];
								for(let i = 0; i < arr.length; i++){
									responseObj = responseObj[arr[i]];
								}
								
								if(responseObj && responseObj.preview_title){
									responseObj.preview_title = responseObj.preview_title.substring(0, 50);
									if(this.containsAnyChar(responseObj.preview_title)){  //判断是否含有特殊字符
										responseObj.preview_title = responseObj.preview_title.replace(/[\\/:*?"<>|#%&\s]/g, "");
									}
								}
								if(responseObj && responseObj.video && responseObj.video.play_addr){
									let vinfo = {
										title: responseObj.preview_title,
										tag: 'douyin',
										format_id: 'default',
										ext: 'mp4',
										resolution: responseObj.video.play_addr.width + 'x' + responseObj.video.play_addr.height,
										fps: '-',
										filesize: responseObj.video.play_addr.data_size, 
										vcodec: '-',
										acodec: '-',
										urlList: responseObj.video.play_addr.url_list,
										status: '1',
										loading: false
									}
									this.videoList.push(vinfo);
								}
								if(responseObj && responseObj.video && responseObj.video.play_addr_265){
									let vinfo = {
										title: responseObj.preview_title,
										tag: 'douyin',
										format_id: 'play_addr_265',
										ext: 'mp4',
										resolution: responseObj.video.play_addr_265.width + 'x' + responseObj.video.play_addr_265.height,
										fps: '-',
										filesize: responseObj.video.play_addr_265.data_size,
										vcodec: '-',
										acodec: '-',
										urlList: responseObj.video.play_addr_265.url_list,
										status: '1',
										loading: false
									}
									this.videoList.push(vinfo);
								}
								if(responseObj && responseObj.video && responseObj.video.play_addr_h264){
									let vinfo = {
										title: responseObj.preview_title,
										tag: 'douyin',
										format_id: 'play_addr_h264',
										ext: 'mp4',
										resolution: responseObj.video.play_addr_h264.width + 'x' + responseObj.video.play_addr_h264.height,
										fps: '-',
										filesize: responseObj.video.play_addr_h264.data_size,
										vcodec: '-',
										acodec: '-',
										urlList: responseObj.video.play_addr_h264.url_list,
										status: '1',
										loading: false
									}
									this.videoList.push(vinfo);
								}
							}else if(responseVideo.length > 0){ //网页解析到视频地址
								let vinfo = {
									title: vtitle,
									tag: 'douyin',
									format_id: 'video',
									ext: 'mp4',
									resolution: '-',
									fps: '-',
									filesize: '',
									vcodec: '-',
									acodec: '-',
									urlList: responseVideo,
									status: '1',
									loading: false
								}
								this.videoList.push(vinfo);
							}
							
							
							
							this.parseLoading = false;
							this.tabLoading = false;
						}catch(e){
							this.parseLoading = false;
							this.tabLoading = false;
							reject(e);
							this.showError(e);
						}
					})();
				});
				
			},
			
			// 解析视频-快手-其他
			async videoParsing(url){
				if(this.loginBrowser){
					await this.loginBrowser.close();
					this.loginBrowser = null;
				}
				if(this.videoBrowser){
					await this.videoBrowser.close();
					this.videoBrowser = null;
				}
				if (!fs.existsSync(os.tmpdir() + separator + 'chrome-data-capture-video')) {
					fs.mkdirSync(os.tmpdir() + separator + 'chrome-data-capture-video');
				}
				
				this.parseLoading = true;
				this.tabLoading = true;
				this.videoList = [];
				
				setTimeout(()=> {
					this.parseLoading = false;
					this.tabLoading = false;
				}, 30000)
				
				let userDataDir = os.tmpdir() + separator + 'chrome-data-capture-video';
				// 运行不同平台的浏览器
				puppeteer.use(StealthPlugin());
				let headless = true;
				headless = this.initDevelop().headless;
				this.videoBrowser = await puppeteer.launch({
					headless: headless,
					executablePath: this.initPath(),
					userDataDir: userDataDir, 
					args: [
						'--start-maximized',
						'--no-sandbox',
						'--disable-setuid-sandbox',
						'--disable-blink-features=AutomationControlled',
					]
				});
				
				await new Promise((resolve,reject) =>{
					(async () => {
						try{
							let authority = this.$refs.headerRef.authority.isAuthority;
							const page = await this.videoBrowser.newPage();
							
							let responseVideo = [];
							page.on('response', async(response) => {
								// 检查响应的 MIME 类型是否以 'video/' 开头
								if (response.headers()['content-type'] && response.headers()['content-type'].startsWith('video/')) {
									if(responseVideo.indexOf(response.url()) < 0 && !response.url().startsWith('blob:https://')){
										
										let title = await page.title();
										if(title){
											title = title.substring(0, 50);
											if(this.containsAnyChar(title)){  //判断是否含有特殊字符
												title = title.replace(/[\\/:*?"<>|#%&\s]/g, "");
											}
										}
										
										let vinfo = {
											title: title,
											tag: 'other',
											format_id: 'default',
											ext: 'mp4',
											resolution: '-',
											fps: '-',
											filesize: response.headers()['content-length'], 
											vcodec: '-',
											acodec: '-',
											urlList: [response.url()],
											status: '1',
											loading: false
										}
										responseVideo.push(response.url());
										this.videoList.push(vinfo);
									}
								}
								
							});
							let waitUntil = 'networkidle2';
							waitUntil = this.initDevelop().waitUntil;
							await page.goto(url, {waitUntil : waitUntil});
							await page.waitForTimeout(1000);
							
							let pageInfo = await page.evaluate(() => {
								let cHeight = document.documentElement.clientHeight;
								let scrollHeight = document.body.scrollHeight;
								return {'scrollHeight': scrollHeight, 'cHeight': cHeight}
							});
							
							let scrollHeight = pageInfo.scrollHeight;
							let cHeight = pageInfo.cHeight;
							
							let num = Math.ceil(scrollHeight / cHeight);
							let start = -1;
							let scrollInt = setInterval(async() => {
								start ++;
								if(start > num || start > 20){ // 防止页面过长，滚动50次自动停止
									clearInterval(scrollInt);
									
									const videoArr = await page.evaluate(() => {
										let video = [];
										// 视频
										let arr5 = document.querySelectorAll('video');
										if(arr5.length == 0){
											arr5 = document.querySelectorAll('video source');
										}
										for(let i=0; i< arr5.length; i++){
											if(video.indexOf(arr5[i].src) < 0 && !arr5[i].src.startsWith('blob:https://')){
												if(arr5[i].src){
													video.push(arr5[i].src);
												}
											}
										}
										return video;
									});
									
									if(videoArr.length > 0){
										let title = await page.title();
										if(title){
											title = title.substring(0, 50);
											if(this.containsAnyChar(title)){  //判断是否含有特殊字符
												title = title.replace(/[\\/:*?"<>|#%&\s]/g, "");
											}
										}
										
										videoArr.map((item, index) => {
											let vinfo = {
												title: title,
												tag: 'other',
												format_id: index,
												ext: 'mp4',
												resolution: '-',
												fps: '-',
												filesize: '-', 
												vcodec: '-',
												acodec: '-',
												urlList: [item],
												status: '1',
												loading: false
											}
											
											let skipFlag = true;
											for(let j = 0; j < this.videoList.length; j++){
												if(this.videoList[j].urlList[0] == item){
													skipFlag = false;
												}
											}
											if(skipFlag){
												this.videoList.push(vinfo);
											}
										});
									}
									await this.videoBrowser.close();
									this.parseLoading = false;
									this.tabLoading = false;
								}
							}, 600);
							
							this.parseLoading = false;
							this.tabLoading = false;
						}catch(e){
							this.parseLoading = false;
							this.tabLoading = false;
							reject(e);
							this.showError(e);
						}
					})();
				});
				
			},
			
			// 下载网址链接的图片
			async downloadImage(imageUrl, outputPath, urlInfo) {
				let _this = this;
				let received_bytes = 0;
				let total_bytes = 0;
				try {
					let req = request({
					    method: 'GET', uri: imageUrl, strictSSL: false
					});
					let out = fs.createWriteStream(outputPath);
					req.pipe(out);
					
					return new Promise((resolve, reject) => { 
						req.on('response', (data) => {
							total_bytes = parseInt(data.headers['content-length']);
							const status = data.statusCode;
							if(this.menuIndex != '1'){
								if (status < 200 || status >= 300) {
									this.$notify.error({
										title: '网络资源访问异常！- 1',
										message: imageUrl.slice(0,50)
									});
								}else if(isNaN(total_bytes)){
									this.$notify.error({
										title: '网络资源访问异常！- 2',
										message: imageUrl.slice(0,50)
									});
								}else{
									// console.log('下载中...')
								}
							}
							
							if(status == 403 || isNaN(total_bytes)){
								fs.unlinkSync(outputPath);
								urlInfo.status = '4';
							}
							
						});

						req.on('data', (chunk) => {
						    received_bytes += chunk.length;
							if(urlInfo.filesize){
								let size = Number(urlInfo.filesize);
								let percent = Number(received_bytes / size * 100);
								urlInfo.percent = percent.toFixed(2);
								if(percent > 100){
									urlInfo.percent = 100;
								}
								this.$forceUpdate();
							}
						});
						
						req.on('end', ()=> {
							urlInfo.status = '3';
							//console.log('下载完成', outputPath)
							resolve(true);
						});
					});
			    } catch (error) {  
					urlInfo.status = '4';
			        console.error(imageUrl, `Failed to download image: ${error.message}`);  
			        throw error;  
			    }  
			},
			
			// 获取页面标题 - 生成对应的文件夹
			async getTitle(page, urlInfo){
				// 已页面标题作为新建文件夹,保留前50个字
				let title = await page.title();
				if(title){
					title = title.substring(0, 50);
					if(this.containsAnyChar(title)){  //判断是否含有特殊字符
						title = title.replace(/[\\/:*?"<>|#%&\s]/g, "");
					}
					if (fs.existsSync(this.downloadDir + separator + pjson.softInfo.softName + separator + title)) {
						urlInfo.newPath = this.downloadDir + separator + pjson.softInfo.softName + separator + title;
					} else {
						fs.mkdirSync(this.downloadDir + separator + pjson.softInfo.softName + separator + title);
						urlInfo.newPath = this.downloadDir + separator + pjson.softInfo.softName + separator + title;
					}
				}
			},
			
			// 下载base64位的图片
			async downloadBaseImage(base64String, outputPath, urlInfo) {
				const buffer = Buffer.from(base64String, 'base64');
				const writeStream = fs.createWriteStream(outputPath);
				writeStream.write(buffer);  
				writeStream.end();
				writeStream.on('finish', () => {
					urlInfo.num += 1;
				});
				// 监听错误事件  
				writeStream.on('error', (err) => {  
					console.error('base64位写入文件时出错:', err);  
				});
			},
			
			// 错误提示
			showError(e){
				let str = '';
				if(e.toString().indexOf('ERR_NAME_NOT_RESOLVE') > -1){
					str = '无法解析该网址，请查看网址是否正确！-1';
				}else if(e.toString().indexOf('Cannot navigate to invalid URL') > -1){
					str = '无效的网址，请查看网址格式是否正确！-2';
				}else if(e.toString().indexOf('ERR_CONNECTION_TIMED_OUT') > -1){
					str = '链接请求超时，请查看网络状态！-3';
				}else if(e.toString().indexOf('TimeoutError') > -1){
					str = '链接请求超时，请查看网络状态！-4';
				}else if(e.toString().indexOf('operation not permitted') > -1){
					str = '权限受限，请以管理员权限运行软件！-5';
				}else if(e.toString().indexOf('browser has disconnected') > -1){
					str = '内置浏览器已关闭！-6';
				}else if(e.toString().indexOf('Failed to launch the browser') > -1){
					str = '请先关闭内置浏览器！-7';
				}else{
					str = e.toString();
					//console.log(e);
				}
				this.loading = false;
				this.$notify.error({
					title: '提示',
					message: str
				});
			},
			
			// 是否包含特殊字符
			containsAnyChar(str) {
				let charsArray = ['\\', '/', ':', '*', '?', '"', '<', '>', '|', '#', '%' ,'&', ' ', '\t', '\n'];
			    for (let i = 0; i < charsArray.length; i++) {
			        if (str.includes(charsArray[i])) {  
			            return true;
			        }  
			    }  
			    return false; 
			},
			
			// 随机生成字符
			randomString(length) {  
			    let result  = '';  
			    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';  
			    const charactersLength = characters.length;  
			    for (let i = 0; i < length; i++ ) {
			       result += characters.charAt(Math.floor(Math.random() * charactersLength));  
			    }  
			    return result;  
			},
			
			//
			formatSeconds(value) {
				var secondTime = parseInt(value); // 秒
				var minuteTime = 0; // 分
				var hourTime = 0; // 小时
				if (secondTime >= 60) {
					minuteTime = parseInt(secondTime / 60);
					secondTime = parseInt(secondTime % 60);
					if (minuteTime >= 60) {
						hourTime = parseInt(minuteTime / 60);
						minuteTime = parseInt(minuteTime % 60);
					}
				}
			
				var result ="" +(parseInt(secondTime) < 10? "0" + parseInt(secondTime): parseInt(secondTime));
				result ="" + (parseInt(minuteTime) < 10? "0" + parseInt(minuteTime) : parseInt(minuteTime)) + ":" + result;
				result ="" + (parseInt(hourTime) < 10 ? "0" + parseInt(hourTime): parseInt(hourTime)) +":" + result;
				return result;
			},
			
			// 视频时间转为秒
			getSs(rawDuration){
				const $ar = rawDuration.split(":");
				let $duration = parseFloat($ar[2]);
				if ($ar[1]) $duration += parseInt($ar[1]) * 60;
				if ($ar[0]) $duration += parseInt($ar[0]) * 60 * 60;
				
				return $duration;
			},
			
		}
	};
</script>

<style lang="scss">
	@import "../assets/css/fontx/iconfont.css";
	@import "../assets/css/home.scss";

	.ivu-input-number-controls-outside-btn i {
		font-weight: 800;
	}

	.update-point {
		display: inline-block;
		width: 8px;
		height: 8px;
		border-radius: 8px;
		background: #ff0000;
		top: 14px;
		position: absolute;
		left: -13px;
	}

	.menu-item {
		padding: 8px 0;
		font-size: 14px;

		.iconfont {
			font-size: 32px;
		}

		&:hover,
		&.active {
			color: #ed4014;
		}
	}

	.ivu-progress-show-info .ivu-progress-outer {
		padding-right: 40px !important;
		margin-right: -40px !important;
	}

	.tips {
		text-align: center;
		padding: 10px 0;
		color: #ed4014;
		font-size: 12px;
	}

	.handle-desc {
		display: inline-block;
		width: calc(100% - 100px);
		overflow: hidden;
	}

	.ivu-menu-submenu-title {
		font-weight: 600;
	}

	.ivu-menu .ivu-menu-item {
		line-height: 1;
	}

	// new-el
	.el-menu {
		border-right: none !important;
	}

	.cmenu-item {
		padding: 0 20px 20px;
		margin-bottom: 15px;

		.cmenu-title {
			font-size: 18px;
			font-weight: 600;
			padding-bottom: 20px;
		}

		.citem-nav {
			text-align: center;
			border-radius: 10px;
			min-height: 110px;
			padding: 15px 0;
			background-color: #fff;
			font-size: 15px;
			cursor: pointer;

			&.bg-linear1 {
				color: #fff;
				font-size: 20px;
				background: linear-gradient(to right bottom, #2A56CA, #5795F4);
			}

			&.bg-linear2 {
				color: #fff;
				font-size: 20px;
				background: linear-gradient(to right top, #147FBB, #5EB3E3);
			}

			&.bg-linear3 {
				color: #fff;
				font-size: 20px;
				background: linear-gradient(to right bottom, #2F9E8A, #56CDB1);
			}

			&:hover {
				margin-top: -5px;
				box-shadow: 3px 3px 6px #ccc, -3px -3px 6px #ccc;
			}

			.citem-img {
				width: 50px;
				margin-bottom: 10px;
			}
		}
	}
	
	.popper-open{
		text-align: center !important;
		padding: 10px !important;
		background: #303133 !important;
		color: #fff !important;
		min-width: 120px !important;
		opacity: 0.8;
	}
	
	.popper-open[x-placement^=bottom] .popper__arrow::after{
		border-bottom-color: #303133 !important;
	}
	
	textarea.el-textarea__inner{
		height: 100% !important;
		font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
	}
	
	.set-item{
		margin-right: 15px;
		
		.set-title{
			font-size: 13px;
		}
	}
	
	.outarea{
		height: 100%;
		border: 1px solid #DCDFE6;
		background-color: #4851a415; 
		padding: 15px;
		font-size: 20px;
		overflow: hidden auto;
	}
	
	.outtext{
		height: 100%;
		font-size: 20px;
		overflow: hidden auto;
		
		textarea{
			background-color: #4851a415; 
		}
	}
	
	.pin-tips{
		font-size: 14px;
		position: absolute;
		bottom: 10px;
		color: #ff0000;
		left: 0;
		right: 0;
		margin: auto;
		text-align: center;
	}
	
	.outarea.red-border{
		border: 1px solid #F56C6C;
	}
	
	.outtext.red-border textarea{
		border: 1px solid #F56C6C;
	}
	
	h3{
		margin: 0;
	}
	
	.m-image{
		width: 20px;
		margin-right: 5px;
	}
	
	.dialog-footer-center{
		text-align: center;
	}
	
	.visible-tips-style{
		font-size: 18px; 
		color: #f73131; 
		font-weight: 600; 
		padding: 30px 40px 0;
	}
	
	.no-select{
		user-select: none; 
	}
	
	.tips-flex{
		display: flex;
		flex-wrap: nowrap;
		justify-content: space-around;
		align-items: center;
		
		.el-icon-s-opportunity{
			font-size: 50px;
			color: #f73131;
			margin-right: 10px;
		}
		
		.m-title{
			flex: 1;
			color: #f73131;
		}
	}
	
	.c-titles{
		background: -webkit-linear-gradient(left, #FF993F, #FFCE7C 25%, #FF9682 50%, #8e430d 75%, #FF993F);
		color: transparent;
		-webkit-background-clip: text;
		background-size: 200% 100%;
		animation: mask-ani 6s infinite linear;
		font-size: 30px;
		font-weight: 600;
		line-height: 1.5;
	}
	
	@-webkit-keyframes mask-ani {
	    0% {
	        background-position: 0 0;
	    }
	
	    50% {
	        background-position: 100% 0;
	    }
	
	    100% {
	        background-position: 200% 0;
	    }
	}
	
	.soft-icon{
		margin: 0 3px;
	}
	
	.el-upload-list{
		display: none;
	}
	
</style>