first commit
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.parcel-cache
|
||||
dist
|
||||
package-lock.json
|
||||
node_modules
|
||||
39
downloader.js
Normal file
39
downloader.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const { spawn } = require('child_process');
|
||||
const logger = require('./lib/logger');
|
||||
const settings = require('./settings.json');
|
||||
|
||||
const download = (socket, url) => {
|
||||
const ytldp = spawn('./lib/yt-dlp.exe',
|
||||
['-o', `${settings.download_path}%(title)s.%(ext)s`, url]
|
||||
)
|
||||
|
||||
ytldp.stdout.on('data', data => {
|
||||
socket.emit('progress', data.toString())
|
||||
console.log(data.toString())
|
||||
})
|
||||
ytldp.on('exit', () => {
|
||||
socket.emit('progress', 'Done!')
|
||||
})
|
||||
}
|
||||
|
||||
const abortDownload = (socket) => {
|
||||
const res = process.platform === 'win32' ?
|
||||
spawn('taskkill', ['/IM', 'yt-dlp.exe', '/F', '/T']) :
|
||||
spawn('killall', ['yt-dlp'])
|
||||
res.stdout.on('data', data => {
|
||||
socket.emit('progress', 'Aborted!')
|
||||
console.log(data.toString())
|
||||
})
|
||||
logger('download', 'Aborted')
|
||||
}
|
||||
|
||||
const kill = async () => {
|
||||
return process.platform === 'win32' ?
|
||||
spawn('taskkill', ['/IM', 'yt-dlp.exe', '/F', '/T']) :
|
||||
spawn('killall', ['yt-dlp'])
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
download: download,
|
||||
abortDownload: abortDownload
|
||||
}
|
||||
5
frontend/index.css
Normal file
5
frontend/index.css
Normal file
@@ -0,0 +1,5 @@
|
||||
body{
|
||||
height: 100vh;
|
||||
background-color: #202124;
|
||||
color: #f1f1f1;
|
||||
}
|
||||
19
frontend/index.html
Normal file
19
frontend/index.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
<title>Frontend</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="index.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
5
frontend/index.js
Normal file
5
frontend/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import ReactDOM from 'react-dom'
|
||||
import { App } from './src/App'
|
||||
|
||||
const root = document.getElementById('root')
|
||||
ReactDOM.render(<App />, root)
|
||||
0
frontend/src/App.css
Normal file
0
frontend/src/App.css
Normal file
69
frontend/src/App.jsx
Normal file
69
frontend/src/App.jsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import { io } from "socket.io-client";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Container, ProgressBar, InputGroup, FormControl, Button } from "react-bootstrap";
|
||||
import './App.css'
|
||||
|
||||
const socket = io('http://localhost:3000')
|
||||
|
||||
export function App() {
|
||||
useEffect(() => {
|
||||
socket.on('progress', data => {
|
||||
setMessage(data.trim())
|
||||
if (data.trim() === 'Done!') {
|
||||
setHalt(false)
|
||||
setProgress(0)
|
||||
}
|
||||
try {
|
||||
setProgress(Math.ceil(data.split(" ")[2].replace('%', '')))
|
||||
} catch (error) {
|
||||
console.log('finished or empty url')
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
const sendUrl = () => {
|
||||
setHalt(true)
|
||||
console.log(url)
|
||||
socket.emit('send-url', url)
|
||||
}
|
||||
|
||||
const handleUrlChange = (e) => {
|
||||
setUrl(e.target.value)
|
||||
}
|
||||
|
||||
const abort = () => {
|
||||
socket.emit('abort')
|
||||
setHalt(false)
|
||||
}
|
||||
|
||||
const [progress, setProgress] = useState(0)
|
||||
const [message, setMessage] = useState('')
|
||||
const [halt, setHalt] = useState(false)
|
||||
const [url, setUrl] = useState('')
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<div className="mt-5" />
|
||||
<h1>yt-dlp web ui</h1>
|
||||
|
||||
<InputGroup className="mt-5">
|
||||
<FormControl placeholder="youtube video url" onChange={handleUrlChange} />
|
||||
</InputGroup>
|
||||
|
||||
<div className="mt-2">
|
||||
<h6>Status</h6>
|
||||
<pre id='status'>{message}</pre>
|
||||
</div>
|
||||
|
||||
<ProgressBar now={progress} />
|
||||
|
||||
<Button className="my-5" variant="success" onClick={() => sendUrl()} disabled={halt}>Go!</Button>{' '}
|
||||
<Button variant="danger" onClick={() => abort()}>Abort</Button>
|
||||
|
||||
<div className="mt-5" />
|
||||
<div>
|
||||
Once you close the page the download will continue in the background. It won't be possible retriving the progress though.
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
19
lib/event.js
Normal file
19
lib/event.js
Normal file
@@ -0,0 +1,19 @@
|
||||
var Transform = require('stream').Transform;
|
||||
|
||||
class SSE extends Transform{
|
||||
constructor(options) {
|
||||
super();
|
||||
|
||||
if (!(this instanceof SSE))
|
||||
return new SSE(options);
|
||||
|
||||
options = options || {};
|
||||
Transform.call(this, options);
|
||||
}
|
||||
_transform(data, enc, cb) {
|
||||
this.push('data: ' + data.toString('utf8') + '\n\n');
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SSE;
|
||||
5
lib/logger.js
Normal file
5
lib/logger.js
Normal file
@@ -0,0 +1,5 @@
|
||||
const logger = (proto, args) => {
|
||||
console.log(`[${proto}] ${args}`)
|
||||
}
|
||||
|
||||
module.exports = logger
|
||||
BIN
lib/yt-dlp.exe
Normal file
BIN
lib/yt-dlp.exe
Normal file
Binary file not shown.
24
lib/yt-dlpCaller.js
Normal file
24
lib/yt-dlpCaller.js
Normal file
@@ -0,0 +1,24 @@
|
||||
const Readable = require('stream').Readable;
|
||||
|
||||
class Subscription extends Readable{
|
||||
constructor(options) {
|
||||
super();
|
||||
if (!(this instanceof Subscription))
|
||||
return new Subscription(options);
|
||||
|
||||
options = options || {};
|
||||
Readable.call(this, options);
|
||||
|
||||
this.value = 0;
|
||||
}
|
||||
_read() {
|
||||
while(this.value <= 100){
|
||||
this.push(String(this.value++));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
exports.subscribe = function(event, options){
|
||||
return new Subscription(options);
|
||||
}
|
||||
26
package.json
Normal file
26
package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "youtube-dlp-web",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "node --harmony app.js",
|
||||
"dev": "nodemon app.js",
|
||||
"build": "parcel build ./frontend/index.html",
|
||||
"fe": "parcel ./frontend/index.html --open"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@koa/cors": "^3.1.0",
|
||||
"koa": "^2.13.4",
|
||||
"koa-router": "^10.1.1",
|
||||
"koa-static": "^5.0.0",
|
||||
"koa-views": "^7.0.2",
|
||||
"parcel": "^2.0.1",
|
||||
"react": "^17.0.2",
|
||||
"react-bootstrap": "^2.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"socket.io": "^4.3.2",
|
||||
"socket.io-client": "^4.3.2"
|
||||
}
|
||||
}
|
||||
42
server.js
Normal file
42
server.js
Normal file
@@ -0,0 +1,42 @@
|
||||
const Koa = require('koa');
|
||||
const serve = require('koa-static');
|
||||
const { Server } = require('socket.io');
|
||||
const path = require('path');
|
||||
const { createServer } = require('http');
|
||||
const cors = require('@koa/cors');
|
||||
const logger = require('./lib/logger');
|
||||
const { download, abortDownload } = require('./downloader');
|
||||
|
||||
const app = new Koa()
|
||||
const server = createServer(app.callback())
|
||||
const io = new Server(server, {
|
||||
cors: {
|
||||
origin: "*",
|
||||
methods: ["GET", "POST"]
|
||||
}
|
||||
})
|
||||
|
||||
io.on('connection', socket => {
|
||||
logger('ws', 'connesso')
|
||||
|
||||
socket.on('send-url', args => {
|
||||
logger('ws', args)
|
||||
download(socket, args)
|
||||
})
|
||||
|
||||
socket.on('abort', () => {
|
||||
abortDownload(socket)
|
||||
})
|
||||
});
|
||||
|
||||
io.on('disconnect', () => {
|
||||
logger('ws', 'disconnesso')
|
||||
});
|
||||
|
||||
app
|
||||
.use(cors())
|
||||
.use(serve(path.join(__dirname, 'dist')))
|
||||
|
||||
console.log('[koa] Server started port', 3000)
|
||||
|
||||
server.listen(3000)
|
||||
3
settings.json
Normal file
3
settings.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"download_path": "C:\\Users\\marco\\Downloads\\"
|
||||
}
|
||||
Reference in New Issue
Block a user