修改为前后端分离模式

This commit is contained in:
Theluyuan 2023-06-14 15:43:04 +08:00
parent 029557a5c0
commit 7ded5ba751
16 changed files with 989 additions and 531 deletions

View File

@ -1,25 +1,24 @@
const { app, BrowserWindow } = require('electron')
let path = require('path');
const Koa = require('koa');
const app = new Koa();
const static = require('koa-static')
app.use(static("./view/dist"))
const { koaBody } = require('koa-body');
const KoaSSEStream = require('koa-sse-stream');
app.use(koaBody());
app.use(async (ctx, next) => {
ctx.set('Access-Control-Allow-Origin', '*');
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With');
ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (ctx.method == 'OPTIONS') {
ctx.body = 200;
} else {
await next();
}
});
const createWindow = () => {
const win = new BrowserWindow({
width: 1000,
height: 800,
webPreferences: {
preload: path.join(__dirname, '/util/preload.js'),
sandbox: false,
allowRunningInsecureContent: true,
webSecurity: false,
contextIsolation: false
}
})
win.loadFile('./view/dist/index.html')
// win.loadURL("http://localhost:5173/")
}
// app.whenReady().then(() => {
// createWindow()
// })
app.on('ready', function () {
createWindow()
});
const index = require("./router/index.js")
app.use(index.routes()).use(index.allowedMethods())
app.listen(3050, "0.0.0.0");

View File

@ -4,17 +4,20 @@
"description": "",
"main": "index.js",
"scripts": {
"start": "electron ."
"start": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^1.4.0",
"puppeteer": "^20.4.0"
},
"devDependencies": {
"electron": "^25.1.0"
"koa": "^2.14.2",
"koa-body": "^6.0.1",
"koa-router": "^12.0.0",
"koa-sse-stream": "^0.2.0",
"koa-static": "^5.0.0",
"koa-websocket": "^7.0.0",
"puppeteer": "^20.7.1"
},
"bin": "./index.js",
"pkg": {

File diff suppressed because it is too large Load Diff

58
router/index.js Normal file
View File

@ -0,0 +1,58 @@
const Router = require("koa-router")
const { getQqListData } = require("../util/getList/qq")
const KoaSSEStream = require('koa-sse-stream');
const { dowload } = require("../util/download");
const router = new Router()
const dowloadlist = []
router.get("/getQqList",async (ctx)=>{
let url = ctx.query.url
let list = await getQqListData(url)
ctx.body = list
})
router.get("/startDown",async (ctx)=>{
let title = ctx.query.title
let url = ctx.query.url
dowloadlist.push({
title,url
})
// startDown()
ctx.body={
code:0,
msg:"添加成功"
}
})
// 连接池
const clientList = [];
// koa-sse-stream 配置
const SSE_CONF = {
maxClients: 5, // 最大连接数
pingInterval: 40000 // 重连时间
}
router.get('/msg', KoaSSEStream(SSE_CONF), ctx => {
// 每次连接会进行一个 push
clientList.push(ctx.sse);
})
let start = false
function startDown(){
dowload(dowloadlist[0],(data,isok)=>{
clientList.forEach((i)=>{
i.send(isok ? `${dowloadlist[0]}下载完成` : data )
if(isok){
dowloadlist.shift()
}
})
})
}
setInterval(()=>{
if(dowloadlist.length != 0 && !start){
start = true
startDown()
}else if(dowloadlist.length == 0){
start = false
}
},1000)
module.exports = router

23
util/download.js Normal file
View File

@ -0,0 +1,23 @@
const path = require('path')
const { getListData } = require(path.join(__dirname,'/getList/qq.js'));
const {dow} = require("./utils.js")
// async function getlist (url) {
// let list = await getListData(url)
// return list
// // console.log('electron收到远端的传参', a);
// // callback('config result'); // 回调给远端的请求数据,如 config
// // ipcRenderer.send('close', 'args bbb'); // 比如收到请求关闭窗口
// };
function dowload(info,callback){
console.log("开始")
dow(info,callback)
}
module.exports = {
dowload
}

View File

@ -1,7 +1,7 @@
const puppeteer = require("puppeteer")
const { seep } = require("../utils.js");
async function getListData(url) {
async function getQqListData(url) {
const browser = await puppeteer.launch({
// 关闭无头模式,方便我们看到这个无头浏览器执行的过程
headless: false,
@ -16,6 +16,17 @@ async function getListData(url) {
let list = await page.evaluate(() => {
return window.__PINIA__.episodeMain
})
// let qb = await page.$$(".b-btn--round")
await page.$$eval(".b-btn--round", (list) => {
console.log(list)
for (let i of list) {
if (i && i.innerText) {
if ("全部" == i.innerText.trim()) {
i.click()
}
}
}
})
for (let i of list.listData[0].tabs) {
let text = i.text
let more = await page.$(".b-tab__more")
@ -30,6 +41,16 @@ async function getListData(url) {
}
}
}, text)
await page.$$eval(".b-tag-list__tag", (list, text) => {
console.log(list)
for (let i of list) {
if (i && i.innerText) {
if (text == i.innerText.trim()) {
i.click()
}
}
}
}, text)
await seep(1000);
}
@ -59,5 +80,5 @@ async function getListData(url) {
}
module.exports = {
getListData
getQqListData
}

View File

@ -1,20 +0,0 @@
const path = require('path')
const { getListData } = require(path.join(__dirname,'/getList/qq.js'));
const {dow} = require("./utils.js")
async function getlist (url) {
let list = await getListData(url)
return list
// console.log('electron收到远端的传参', a);
// callback('config result'); // 回调给远端的请求数据,如 config
// ipcRenderer.send('close', 'args bbb'); // 比如收到请求关闭窗口
};
window.getlist = getlist
window.dowload = function (info,callback){
console.log("开始")
dow(info,callback)
}

View File

@ -12,7 +12,8 @@ function seep(time){
function dow(info,callback){
const lux = spawn("./bin/yt-dlp.exe", ['--cookies-from-browser', 'edge', '-P', 'D:/dl', '-o', info.title + '.mp4', info.url])
lux.stdout.on('data', (data) => {
callback(data,false)
console.log(String(data))
callback(String(data),false)
});
lux.on('close', (code) => {
console.log("结束")

107
view/dist/assets/index-54b3342f.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{_ as S,r as u,a as i,o as p,c as f,b as t,w as s,d as v,e as g,F as q,f as B,E as N,g as U,t as D}from"./index-7cf9519d.js";const F={class:"url"},L={style:{display:"flex",width:"100%"}},R={style:{display:"flex"}},T={style:{width:"100%",height:"100%","overflow-y":"auto"}},j={__name:"index",setup(z){const d=u(!1),n=u({name:"https://v.qq.com/x/cover/mzc002007vp35qj.html",start:1}),c=u([]),r=u();function h(){let l=N.service();window.getlist(n.value.name).then(e=>{console.log(e),c.value=e,l.close()})}function x(){r.value.clearSelection();for(let l=n.value.start-1;l<c.value.length;l++)console.log(l),r.value.toggleRowSelection(c.value[l],void 0)}const y=u([]);function b(l,e,o){console.log(o),o?l():y.value.unshift(e)}function V(l){return new Promise(e=>{const o=b.bind(this,e);window.dowload(l,o)})}async function k(){d.value=!0;let l=r.value.getSelectionRows();for(let e of l)await V(e);d.value=!1,U({showClose:!0,message:"下载完成",type:"success",duration:0})}return(l,e)=>{const o=i("el-input"),_=i("el-button"),w=i("el-form-item"),m=i("el-table-column"),C=i("el-table"),E=i("el-dialog");return p(),f("div",F,[t(w,{label:"视频地址"},{default:s(()=>[v("div",L,[t(o,{style:{width:"100%"},modelValue:n.value.name,"onUpdate:modelValue":e[0]||(e[0]=a=>n.value.name=a)},null,8,["modelValue"]),t(_,{style:{"flex-shrink":"0",margin:"0 10px"},onClick:h,type:"primary"},{default:s(()=>[g("获取")]),_:1})])]),_:1}),t(w,{label:"第几集开始"},{default:s(()=>[v("div",R,[t(o,{modelValue:n.value.start,"onUpdate:modelValue":e[1]||(e[1]=a=>n.value.start=a)},null,8,["modelValue"]),t(_,{style:{"flex-shrink":"0",margin:"0 10px"},onClick:x,type:"primary"},{default:s(()=>[g("设置")]),_:1}),t(_,{style:{"flex-shrink":"0",margin:"0 10px"},onClick:k,type:"primary"},{default:s(()=>[g("下载")]),_:1})])]),_:1}),t(C,{ref_key:"table",ref:r,data:c.value,style:{width:"100%"}},{default:s(()=>[t(m,{type:"selection",width:"55"}),t(m,{label:"title",prop:"title"}),t(m,{label:"url",prop:"url"})]),_:1},8,["data"]),t(E,{fullscreen:!0,"close-on-click-modal":!1,"close-on-press-escape":!1,modelValue:d.value,"onUpdate:modelValue":e[2]||(e[2]=a=>d.value=a),"show-close":!1,title:"Tips",width:"30%"},{default:s(()=>[v("div",T,[(p(!0),f(q,null,B(y.value,a=>(p(),f("div",null,D(a),1))),256))])]),_:1},8,["modelValue"])])}}},M=S(j,[["__scopeId","data-v-ce0e7241"]]);export{M as default};
import{_ as E,r as u,a as i,o as p,c as f,b as t,w as s,d as v,e as g,F as q,f as B,E as D,g as N,t as U}from"./index-54b3342f.js";const F={class:"url"},L={style:{display:"flex",width:"100%"}},R={style:{display:"flex"}},T={style:{width:"100%",height:"100%","overflow-y":"auto"}},j={__name:"index",setup(z){const d=u(!1),n=u({name:"https://v.qq.com/x/cover/mzc002007vp35qj.html",start:1}),c=u([]),r=u();function x(){let l=D.service();window.getlist(n.value.name).then(e=>{console.log(e),c.value=e,l.close()})}function h(){r.value.clearSelection();for(let l=n.value.start-1;l<c.value.length;l++)console.log(l),r.value.toggleRowSelection(c.value[l],void 0)}const y=u([]);function b(l,e,o){console.log(o),o?l():y.value.unshift(e)}function V(l){return new Promise(e=>{const o=b.bind(this,e);window.dowload(l,o)})}async function k(){d.value=!0;let l=r.value.getSelectionRows();for(let e of l)await V(e);d.value=!1,N({showClose:!0,message:"下载完成",type:"success",duration:0})}return(l,e)=>{const o=i("el-input"),_=i("el-button"),w=i("el-form-item"),m=i("el-table-column"),C=i("el-table"),S=i("el-dialog");return p(),f("div",F,[t(w,{label:"视频地址"},{default:s(()=>[v("div",L,[t(o,{style:{width:"100%"},modelValue:n.value.name,"onUpdate:modelValue":e[0]||(e[0]=a=>n.value.name=a)},null,8,["modelValue"]),t(_,{style:{"flex-shrink":"0",margin:"0 10px"},onClick:x,type:"primary"},{default:s(()=>[g("获取")]),_:1})])]),_:1}),t(w,{label:"第几集开始"},{default:s(()=>[v("div",R,[t(o,{modelValue:n.value.start,"onUpdate:modelValue":e[1]||(e[1]=a=>n.value.start=a)},null,8,["modelValue"]),t(_,{style:{"flex-shrink":"0",margin:"0 10px"},onClick:h,type:"primary"},{default:s(()=>[g("设置")]),_:1}),t(_,{style:{"flex-shrink":"0",margin:"0 10px"},onClick:k,type:"primary"},{default:s(()=>[g("下载")]),_:1})])]),_:1}),t(C,{ref_key:"table",ref:r,data:c.value,style:{width:"100%"}},{default:s(()=>[t(m,{type:"selection",width:"55"}),t(m,{label:"title",prop:"title"}),t(m,{label:"url",prop:"url"})]),_:1},8,["data"]),t(S,{fullscreen:!0,"close-on-click-modal":!1,"close-on-press-escape":!1,modelValue:d.value,"onUpdate:modelValue":e[2]||(e[2]=a=>d.value=a),"show-close":!1,title:"Tips",width:"30%"},{default:s(()=>[v("div",T,[(p(!0),f(q,null,B(y.value,a=>(p(),f("div",null,U(a),1))),256))])]),_:1},8,["modelValue"])])}}},M=E(j,[["__scopeId","data-v-550c913f"],["__file","D:/xiangmu/ptSend/view/src/page/index.vue"]]);export{M as default};

View File

@ -5,8 +5,8 @@
<link rel="icon" type="image/svg+xml" href="./vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
<script type="module" crossorigin src="./assets/index-7cf9519d.js"></script>
<link rel="stylesheet" href="./assets/index-6ef103ac.css">
<script type="module" crossorigin src="./assets/index-54b3342f.js"></script>
<link rel="stylesheet" href="./assets/index-711c0355.css">
</head>
<body>
<div id="app"></div>

View File

@ -9,6 +9,7 @@
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.4.0",
"element-plus": "^2.3.6",
"sass": "^1.63.3",
"vue": "^3.2.47",

View File

@ -1,6 +1,9 @@
lockfileVersion: '6.0'
dependencies:
axios:
specifier: ^1.4.0
version: 1.4.0
element-plus:
specifier: ^2.3.6
version: 2.3.6(vue@3.2.47)
@ -410,6 +413,20 @@ packages:
resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
dev: false
/asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: false
/axios@1.4.0:
resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==}
dependencies:
follow-redirects: 1.15.2
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
dev: false
/binary-extensions@2.2.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
engines: {node: '>=8'}
@ -434,6 +451,13 @@ packages:
optionalDependencies:
fsevents: 2.3.2
/combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: false
/csstype@2.6.21:
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
@ -441,6 +465,11 @@ packages:
resolution: {integrity: sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==}
dev: false
/delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dev: false
/element-plus@2.3.6(vue@3.2.47):
resolution: {integrity: sha512-GLz0pXUYI2zRfIgyI6W7SWmHk6dSEikP9yR++hsQUyy63+WjutoiGpA3SZD4cGPSXUzRFeKfVr8CnYhK5LqXZw==}
peerDependencies:
@ -509,6 +538,25 @@ packages:
dependencies:
to-regex-range: 5.0.1
/follow-redirects@1.15.2:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
dev: false
/form-data@4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: false
/fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@ -574,6 +622,18 @@ packages:
resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
dev: false
/mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
dev: false
/mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
dependencies:
mime-db: 1.52.0
dev: false
/nanoid@3.3.6:
resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@ -602,6 +662,10 @@ packages:
picocolors: 1.0.0
source-map-js: 1.0.2
/proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
dev: false
/readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}

View File

@ -11,7 +11,14 @@
<div style="display: flex;">
<el-input v-model="form.start" />
<el-button style="flex-shrink: 0;margin: 0 10px;" @click="setlist" type="primary">设置</el-button>
</div>
</el-form-item>
<el-form-item label="下载保存地址">
<div style="display: flex;">
<el-input v-model="form.save" />
<el-button style="flex-shrink: 0;margin: 0 10px;" @click="dow" type="primary">下载</el-button>
<el-button style="flex-shrink: 0;margin: 0 10px;" @click="showmsg" type="primary">查看进度</el-button>
</div>
</el-form-item>
@ -21,7 +28,7 @@
</el-table-column>
<el-table-column label="url" prop="url" />
</el-table>
<el-dialog :fullscreen="true" :close-on-click-modal="false" :close-on-press-escape="false" v-model="dialogVisible" :show-close="false" title="Tips" width="30%" >
<el-dialog :fullscreen="true" v-model="dialogVisible" title="Tips" width="30%" >
<div style="width: 100%;height: 100%;overflow-y: auto;">
<div v-for="i in msg">{{ i }}</div>
</div>
@ -33,19 +40,26 @@
<script setup>
import { ref } from 'vue';
import { ElLoading,ElMessage } from 'element-plus'
import axios from 'axios';
const dialogVisible = ref(false)
const form = ref({
name: "https://v.qq.com/x/cover/mzc002007vp35qj.html",
start: 1
start: 1,
save:"D:/dl"
})
const tableData = ref([])
const table = ref()
function getlist() {
let load = ElLoading.service()
window.getlist(form.value.name).then((res) => {
console.log(res)
tableData.value = res
load.close()
// let load = ElLoading.service()
// window.getlist(form.value.name).then((res) => {
// console.log(res)
// tableData.value = res
// load.close()
// })
axios.get("http://127.0.0.1:3050/getQqList",{
params: {url:form.value.name}
}).then((r)=>{
tableData.value = r.data
})
}
function setlist() {
@ -55,39 +69,32 @@ function setlist() {
table.value.toggleRowSelection(tableData.value[i], undefined)
}
}
function handleSelectionChange(i) {
console.log(i)
}
const msg = ref([])
function log(res,i,end) {
console.log(end)
if(end){
res()
}else{
msg.value.unshift(i)
}
}
function down(info) {
return new Promise((res) => {
const callback = log.bind(this,res)
window.dowload(info,callback)
})
}
async function dow() {
dialogVisible.value = true
let list = table.value.getSelectionRows()
for(let i of list){
await down(i)
await axios.get("http://127.0.0.1:3050/startDown",{
params:i
})
}
dialogVisible.value = false
// dialogVisible.value = false
ElMessage({
showClose: true,
message: '下载完成',
message: '添加完成',
type: 'success',
duration:0
})
}
const source = new EventSource(`http://127.0.0.1:3050/msg`);
source.onmessage = event => {
//
msg.value.unshift(event.data)
}
function showmsg(){
dialogVisible.value = true
}
</script>
<style lang="scss" scoped>