Big update! Websocket!

This commit is contained in:
loicbersier 2020-06-11 16:21:55 +02:00
parent 706c891e08
commit dc0db93490
17 changed files with 555 additions and 102 deletions

View file

@ -3,15 +3,15 @@ const youtubedl = require('youtube-dl')
const fs = require('fs') const fs = require('fs')
const ffmpeg = require('fluent-ffmpeg') const ffmpeg = require('fluent-ffmpeg')
const { version } = require('../../../package.json'); const { version } = require('../../../package.json');
const Antl = use('Antl') const Ws = use('Ws');
const Antl = use('Antl');
let viewCounter = 0; let viewCounter = 0;
let files = []; let files = [];
let day; let day;
let month; let month;
let announcementArray; let announcementArray;
let announcement let announcement;
function formatBytes(bytes, decimals = 2) { // https://stackoverflow.com/a/18650828 function formatBytes(bytes, decimals = 2) { // https://stackoverflow.com/a/18650828
if (bytes === 0) return '0 Bytes'; if (bytes === 0) return '0 Bytes';
@ -30,17 +30,16 @@ class DownloadController {
async index ({ view, request, locale }) { async index ({ view, request, locale }) {
viewCounter++; viewCounter++;
// Coudln't find a cleaner way to make it change with the browser locale // Coudln't find a cleaner way to make it change with the browser locale
announcementArray = [Antl.forLocale(locale).formatMessage('announcement.1'), Antl.forLocale(locale).formatMessage('announcement.2'), Antl.forLocale(locale).formatMessage('announcement.3'), Antl.forLocale(locale).formatMessage('announcement.4'), Antl.forLocale(locale).formatMessage('announcement.5'), Antl.forLocale(locale).formatMessage('announcement.6')]; announcementArray = [Antl.forLocale(locale).formatMessage('announcement.1'), Antl.forLocale(locale).formatMessage('announcement.2'), Antl.forLocale(locale).formatMessage('announcement.3'), Antl.forLocale(locale).formatMessage('announcement.4'), Antl.forLocale(locale).formatMessage('announcement.5'), Antl.forLocale(locale).formatMessage('announcement.6'), 'Playlist download is experimental'];
// Get random announcement // Get random announcement
announcement = announcementArray[Math.floor(Math.random() * announcementArray.length)]; announcement = announcementArray[Math.floor(Math.random() * announcementArray.length)];
// Get date for some event // Get date for some event
let today = new Date(); let today = new Date();
day = today.getDay(); day = today.getDay();
month = today.getMonth(); month = today.getMonth();
// If legacy link return // If legacy link return
if (request.url() == '/legacy') return view.render('legacy', { version: version, viewCounter: viewCounter, day: day, month: month, announcement: announcement}); if (request.url() == '/legacy') return view.render('legacy', { version: version, viewCounter: viewCounter, day: day, month: month, announcement: announcement});
files = []; files = [];
let file = []; let file = [];
for (let f of fs.readdirSync('./public/uploads')) { for (let f of fs.readdirSync('./public/uploads')) {
@ -48,23 +47,23 @@ class DownloadController {
} }
// get the 5 most recent files // get the 5 most recent files
file = file.sort((a, b) => { file = file.sort((a, b) => {
if ((a || b).endsWith('.mp4') || (a || b).endsWith('.webm') || (a || b).endsWith('.mp3') || (a || b).endsWith('.flac')) { if ((a || b).endsWith('.mp4') || (a || b).endsWith('.webm') || (a || b).endsWith('.mp3') || (a || b).endsWith('.flac') || (a || b).endsWith('.zip')) {
let time1 = fs.statSync(`./public/uploads/${b}`).ctime; let time1 = fs.statSync(`./public/uploads/${b}`).ctime;
let time2 = fs.statSync(`./public/uploads/${a}`).ctime; let time2 = fs.statSync(`./public/uploads/${a}`).ctime;
if (time1 < time2) return -1; if (time1 < time2) return -1;
if (time1 > time2) return 1; if (time1 > time2) return 1;
} }
return 0; return 0;
}).slice(0, 5) }).slice(0, 5)
// Save space by deleting file that doesn't appear in the recent feed // Save space by deleting file that doesn't appear in the recent feed
for (let f of fs.readdirSync('./public/uploads')) { for (let f of fs.readdirSync('./public/uploads')) {
if (!file.includes(f) && (f != 'hidden' && f != '.keep')) { if (!file.includes(f) && (f != 'hidden' && f != '.keep' && f != 'playlist')) {
fs.unlinkSync(`./public/uploads/${f}`); fs.unlinkSync(`./public/uploads/${f}`);
} }
} }
for (let f of file) { for (let f of file) {
if (f.endsWith('.mp4') || f.endsWith('.webm')) { if (f.endsWith('.mp4') || f.endsWith('.webm')) {
// Send file name, file size in MB relative path for the file // Send file name, file size in MB relative path for the file
let fileInfo = formatBytes(fs.statSync(`./public/uploads/${f}`).size).split(' '); let fileInfo = formatBytes(fs.statSync(`./public/uploads/${f}`).size).split(' ');
@ -75,12 +74,12 @@ class DownloadController {
files.push({ name: f.split('.').slice(0, -1).join('.'), size: fileInfo[0], unit: fileInfo[1], date: fs.statSync(`./public/uploads/${f}`).ctime, location: `uploads/${f}`, ext: f.split('.').pop(), img: `/asset/music.png` }); files.push({ name: f.split('.').slice(0, -1).join('.'), size: fileInfo[0], unit: fileInfo[1], date: fs.statSync(`./public/uploads/${f}`).ctime, location: `uploads/${f}`, ext: f.split('.').pop(), img: `/asset/music.png` });
} }
} }
return view.render('index', { version: version, viewCounter: viewCounter, file: files, day: day, month: month, announcement: announcement}); return view.render('index', { version: version, viewCounter: viewCounter, file: files, day: day, month: month, announcement: announcement});
} }
async download({ view, request, response }) { async download({ view, request, response }) {
let page = 'index'; const ws = Ws.getChannel('progress').topic('progress');
if (response.request.url == '/legacy') page = 'legacy';
// To be honest i forgot what it does, but i think i need it // To be honest i forgot what it does, but i think i need it
response.implicitEnd = false response.implicitEnd = false
@ -95,14 +94,10 @@ class DownloadController {
} }
if (!data.url) { if (!data.url) {
return view.render(page, { if (ws) {
version: version, ws.socket.emit('error', 'bruh moment, you didin\'t input a link.');
viewCounter: viewCounter, }
file: files, return;
day: day, month: month, announcement: announcement ,
error: true,
errormsg: 'bruh moment, you didin\'t input a link.'
});
} }
// Youtube-dl quality settings // Youtube-dl quality settings
@ -125,74 +120,175 @@ class DownloadController {
if (err); if (err);
}); });
} }
return youtubedl.exec(data.url, ['--format=mp4', '-o', altFolder], {}, function(err, output) { return youtubedl.exec(data.url, ['--format=mp4', '-o', altFolder], {}, function(err, output) {
if (err) { if (err) {
console.error(err); console.error(err);
return response.send(view.render(page, { if (ws) {
version: version, ws.socket.emit('error', err.toString());
viewCounter: viewCounter, }
file: files, return;
day: day, month: month, announcement: announcement ,
error: true,
errormsg: err
}));
} }
console.log(altFolder.slice(17))
return response.attachment(altFolder); if (ws) {
ws.socket.emit('end', altFolder.slice(17));
}
return;
}); });
} else { } else {
// Download as mp4 if possible if (data.url.match( /^.*(youtu.be\/|list=)([^#\&\?]*).*/)) {
let video = youtubedl(data.url, ['--format=mp4', '-f', option]); playlistDownload(data)
} else {
// Download as mp4 if possible
let video = youtubedl(data.url, ['--format=mp4', '-f', option]);
video.on('error', function(err) { video.on('error', function(err) {
console.error(err);
if (ws) {
ws.socket.emit('error', err.toString());
}
return;
})
let ext;
let size = 0
video.on('info', function(info) {
size = info.size
// Set file name
ext = info.ext;
let title = info.title.slice(0,50);
DLFile = `${title.replace(/\s/g, '_')}.${ext}`;
DLFile = DLFile.replace(/[()]|[/]|[\\]|[?]|[!]/g, '_');
// If no title use the ID
if (title == '_') title = `_${info.id}`;
// If user want to hide from the feed
if (data.feed == 'on')
DLFile = `hidden/${title}.${ext}`;
video.pipe(fs.createWriteStream(`./public/uploads/${DLFile}`));
});
let pos = 0
video.on('data', function data(chunk) {
pos += chunk.length
// `size` should not be 0 here.
if (size) {
let percent = (pos / size * 100).toFixed(2)
if (ws) {
ws.socket.emit('progress', percent);
}
}
})
video.on('end', function() {
console.log('end');
if (ws) {
ws.socket.emit('message', 'end');
}
if (data.format == 'mp4' || data.format == 'webm') {
// If user requested mp4 directly attach the file
if (ws) {
ws.socket.emit('end', DLFile);
}
return;
} else {
// If user requested an audio format, convert it
ffmpeg(`./public/uploads/${DLFile}`)
.noVideo()
.audioChannels('2')
.audioFrequency('44100')
.audioBitrate('320k')
.format(data.format)
.save(`./public/uploads/${DLFile.replace(`.${ext}`, `.${data.format}`)}`)
.on('progress', (progress) => {
wb.broadcast(progress.percent)
})
.on('end', () => {
fs.unlinkSync(`./public/uploads/${DLFile}`);
if (ws) {
ws.socket.emit('end', DLFile.replace(`.${ext}`, `.${data.format}`));
}
});
}
});
}
}
function playlistDownload(data) {
const video = youtubedl(data.url)
video.on('error', function error(err) {
console.error(err); console.error(err);
return response.send(view.render(page, { if (ws) {
version: version, ws.socket.emit('error', err.toString());
viewCounter: viewCounter, }
file: files, return;
day: day, month: month, announcement: announcement , });
error: true,
errormsg: err
}));
})
let ext; let ext;
let size = 0
video.on('info', function(info) { video.on('info', function(info) {
console.log(info);
size = info.size
// Set file name // Set file name
ext = info.ext; ext = info.ext;
let title = info.title.slice(0,50); let title = info.title.slice(0,50);
DLFile = `${title.replace(/\s/g, '_')}.${ext}`; DLFile = `${title.replace(/\s/g, '_')}.${ext}`;
DLFile = DLFile.replace(/[()]|[/]|[\\]/g, '_'); DLFile = DLFile.replace(/[()]|[/]|[\\]|[?]|[!]/g, '_');
// If no title use the ID // If no title use the ID
if (title == '_') title = `_${info.id}`; if (title == '_') title = `_${info.id}`;
// If user want to hide from the feed // If user want to hide from the feed
if (data.feed == 'on') if (data.feed == 'on')
DLFile = `hidden/${title}.${ext}`; DLFile = `hidden/playlist/${title}.${ext}`;
video.pipe(fs.createWriteStream(`./public/uploads/${DLFile}`)); video.pipe(fs.createWriteStream(`./public/uploads/playlist/${DLFile}`));
});
let pos = 0
video.on('data', function data(chunk) {
pos += chunk.length
// `size` should not be 0 here.
if (size) {
let percent = (pos / size * 100).toFixed(2)
process.stdout.cursorTo(0)
process.stdout.clearLine(1)
if (ws) {
ws.socket.emit('progress', percent);
}
}
}); });
video.on('end', function() { video.on('end', function() {
if (data.format == 'mp4' || data.format == 'webm') { if (data.format == 'mp3' || data.format == 'flac') {
// If user requested mp4 directly attach the file
return response.attachment(`./public/uploads/${DLFile}`)
} else {
// If user requested an audio format, convert it // If user requested an audio format, convert it
ffmpeg(`./public/uploads/${DLFile}`) ffmpeg(`./public/uploads/playlist/${DLFile}`)
.noVideo() .noVideo()
.audioChannels('2') .audioChannels('2')
.audioFrequency('44100') .audioFrequency('44100')
.audioBitrate('320k') .audioBitrate('320k')
.format(data.format) .format(data.format)
.save(`./public/uploads/${DLFile.replace(`.${ext}`, `.${data.format}`)}`) .save(`./public/uploads/playlist/${DLFile.replace(`.${ext}`, `.${data.format}`)}`)
.on('end', () => { .on('progress', (progress) => {
fs.unlinkSync(`./public/uploads/${DLFile}`); ws.socket.emit(progress.percent)
return response.attachment(`./public/uploads/${DLFile.replace(`.${ext}`, `.${data.format}`)}`);
}) })
.on('end', () => {
fs.unlinkSync(`./public/uploads/playlist/${DLFile}`);
if (ws) {
ws.socket.emit('end', `./public/uploads/playlist/${DLFile.replace(`.${ext}`, `.${data.format}`)}`);
}
});
} else {
if (ws) {
ws.socket.emit('end', `./public/uploads/playlist/${DLFile}`);
}
} }
}); });
video.on('next', playlistDownload);
} }
} }
} }

View file

@ -0,0 +1,10 @@
'use strict'
class ProgressController {
constructor ({ socket, request }) {
this.socket = socket
this.request = request
}
}
module.exports = ProgressController

66
config/socket.js Normal file
View file

@ -0,0 +1,66 @@
'use strict'
/*
|--------------------------------------------------------------------------
| Websocket Config
|--------------------------------------------------------------------------
|
| Used by AdonisJs websocket server
|
*/
module.exports = {
/*
|--------------------------------------------------------------------------
| Path
|--------------------------------------------------------------------------
|
| The base path on which the websocket server will accept connections.
|
*/
path: '/adonis-ws',
/*
|--------------------------------------------------------------------------
| Server Interval
|--------------------------------------------------------------------------
|
| This interval is used to create a timer for identifying dead client
| connections.
|
*/
serverInterval: 30000,
/*
|--------------------------------------------------------------------------
| Server Attempts
|--------------------------------------------------------------------------
|
| Server attempts are used with serverInterval to identify dead client
| connections. A total of `serverAttempts` attmepts after `serverInterval`
| will be made before terminating the client connection.
|
*/
serverAttempts: 3,
/*
|--------------------------------------------------------------------------
| Client Interval
|--------------------------------------------------------------------------
|
| This interval is used by client to send ping frames to the server.
|
*/
clientInterval: 25000,
/*
|--------------------------------------------------------------------------
| Client Attempts
|--------------------------------------------------------------------------
|
| Clients attempts are number of times the client will attempt to send the
| ping, without receiving a pong from the server. After attempts have
| been elapsed, the client will consider server as dead.
|
*/
clientAttempts: 3
}

View file

@ -1,6 +1,6 @@
{ {
"name": "jeff-downloader", "name": "jeff-downloader",
"version": "0.13.0", "version": "0.14.0",
"adonis-version": "4.1.0", "adonis-version": "4.1.0",
"description": "A video downloader based on youtube-dl", "description": "A video downloader based on youtube-dl",
"main": "server.js", "main": "server.js",
@ -30,8 +30,11 @@
"@adonisjs/session": "^1.0.27", "@adonisjs/session": "^1.0.27",
"@adonisjs/shield": "^1.0.8", "@adonisjs/shield": "^1.0.8",
"@adonisjs/validator": "^5.0.6", "@adonisjs/validator": "^5.0.6",
"@adonisjs/websocket": "^1.0.12",
"@adonisjs/websocket-client": "^1.0.9",
"archiver": "^3.1.1",
"fluent-ffmpeg": "^2.1.2", "fluent-ffmpeg": "^2.1.2",
"mysql": "^2.17.1", "mysql": "^2.18.1",
"node-fetch": "^2.6.0", "node-fetch": "^2.6.0",
"youtube-dl": "^1.13.1" "youtube-dl": "^1.13.1"
}, },

1
public/JS/tis.min.js vendored Normal file
View file

@ -0,0 +1 @@
!function(){var e,t=document,i="addEventListener",o="charCodeAt",n="keyCode",r="keydown",f=0;t[i](r,function(c){f=c[n]=="&&((%'%'BA"[o](f)?f+1:0,!e&&f>9&&(e=3,function(){function f(e){if(k=j[e])k.currentTime=0;else{k=new Uint8Array(9e3),O=50;for(m in k)N=m>1e3?e>>4&63:e>>10,k[m]=127+(O*=1-(15&e)/1e4)*(N/2>m/10%N?-1:1);k=j[y]=c(k)}k.play()}function c(e){return k=e.length,new Audio(URL.createObjectURL(new Blob(["RIFF",new Uint32Array([k+36,1163280727,544501094,16,65537,22050,22050,524289,1635017060,k]),e],{type:"audio/wav"})))}function p(e,t,i,n){return A&&!(-4&e)&&!(-4&t)&&z[o](8*(n||A)-8+2*i+(t>>1))&1<<4*(1&t)+e}function a(){for(B=g;A&&s(h,g+1,w,1););for(L=0;F+4>L;L++)for(T=0;D>T;T++)y=L*D+T,L>=F&&(K[y]=p(T,L-F,0,V[0])?V[0]:0),R[y]=p(T-h,L-B,w)?A:p(T-h,L-g,w)?A+8:K[y]||0,(N=q["tis-"+y])&&(N=N.style,N.background="#"+(1==e&&2>b%4&&x[L]?"fff":Z[R[y]%8]),N.opacity=R[y]>7?.2:1);g=B,B=it+'0;text-align:right;font-size:150%">',q["tis-status"].innerHTML="Score"+B+W+ot+"Lines"+B+X+ot+"Level"+B+_+ot}function s(e,t,i,o){for(m=0;T=3&m,L=m>>2,16>m;m++)if(p(T,L,i)&&((T+=e)<0||T>=D||(L+=t)<0||L>=F||K[L*D+T]))return;return h=e,g=t,w=i,et=0,o||a(),1}function d(t){if(e){if(C=(t-S)/1e3||0,C>.1&&(C=.1),S=t,2==e){if(b-->4&&!(b%4)){for(T=0;D>T;)K[b*D/4+T++]=1+~~(7*I.random());a()}}else if(1==e){if(--b<0){for(L in x)for(y=L*D+D-1;y>=0;y--)K[y]=K[y-D];e=3}a()}else{for(Y in tt){if((37==Y||39==Y)&&tt[Y]>=0&&(tt[Y]-=tt[Y]?.05:.2,s(h+1-2*(37==Y),g,w)),32==Y&&!tt[Y]){for(;s(h,g+1,w););et=9}if((38==Y||18==Y||17==Y)&&(O=1-2*(17==Y),!tt[Y]))for(y=0;5>y;)if(B=(1==A?Q:P)[o]((w+4+(O-1)/2)%4*5+y++)-32,s(h+O*((7&B)-2),g+O*(2-(B>>3)),(w+4+O)%4)){f(8303);break}tt[Y]+=C}if(v+=I.max(tt[40]?.2:0,C*I.pow(1.23,_)),v>1&&(v=0,s(h,g+1,w)),et>1){A&&f(31445);for(y in K)K[y]=R[y];Y=0,x=[];e:for(L=0;F>L;L++){for(T=0;D>T;)if(!K[L*D+T++])continue e;x[L]=e=1,Y++,b=6}if(Y&&f([,8392,8260,8242,8225][Y]),W+=100*[0,1,3,5,8][Y]*_,X+=Y,_=1+~~(X/10),V.length<2)for(B=1;255!=B;){for(m=0;B&1<<m;m=1+~~(7*I.random()));B|=1<<m,V.push(m)}A=V.shift(),v=0,s(3,0,0)||(A=0,e=2,b=4*F,f(31360))}et+=C}requestAnimationFrame(d)}}function u(i){B=i[n],77==B&&J[J.paused?"play":"pause"](),27==B&&(t.body.removeChild(nt),t[M](r,u),t[M](G,l),J.pause(),e=0),tt[B]=tt[B]||0,[17,18,27,37,38,39,40,77].indexOf(B)>=0&&i.preventDefault()}function l(e){delete tt[e[n]]}var A,h,g,w,b,x,C,v,S,y,m,T,L,B,Y,N,O,k,q=window,E="createElement",M="removeEventListener",G="keyup",I=Math,U="LSOSLSOSKSNSKSNS",J=["`+,^,+Y),`.,C,^`\\Yq^.1e31H,01.,C,^`\\Yq","T$$T,+)$),Y))<$TTYTl.).1^..D,\\.,<$TTTTT`","GNKNGNKN"+U+"LSNSL@BCESOSESOSCOCOCOCOBNBN?J?J@CGL@@@@"],$=["xtvstqpsxtvs\\`}|x","tqspqqpptqspY`xx",U+U+U+U],j=[],K=[],R=[],D=10,F=22,H=D*F+20,Z="08080890dd936f9e809dd090e09c0c9f22".split(9),z=atob("8ABERAAPIiJxACYCcAQiA3QAIgZwASMCZgBmAGYAZgA2AGIEYAMxAnIAYgJwAjICYwBkAjAGMgE"),Q="203(C214A,241<!230#8",P='219"!23+BC23;"#21)BA',V=[],W=0,X=0,_=1,et=2,tt=[],it='<div style="margin:',ot="</div>",nt=it+'-14pc -10pc;position:fixed;width:20pc;left:50%;top:50%;font:12px Arial;background:rgba(0,0,0,.8);box-shadow:0 0 2pc #000;border-radius:1pc">'+it+'1pc 2pc;color:#888"><b><a href="http://github.com/ttencate/tis" style="color:inherit">Tis</a></b>: Tetris clone in just 4 kB of JavaScript<br><br>Left/right: move | Up/Ctrl/Alt: rotate | Esc: quit<br>Down/space: soft/hard drop | M: music'+ot+it+'0 1pc;float:right;color:#eee;font-size:1pc"><div id="tis-status">'+ot+"Next"+it+'8px 0;width:4pc">';for(Y=it+'0;width:1pc;height:1pc;float:left;box-shadow:-2px -2px 8px rgba(0,0,0,.4) inset,0 0 2px #000 inset" id="tis-',y=220;H>y;y++)4>y%D&&(nt+=Y+y+'">'+ot);for(nt+=ot+ot+it+'0 2pc 2pc;background:#000;width:10pc;height:20pc">',y=0;H>y;y++)K.push(0),y>19&&220>y&&(nt+=Y+y+'">'+ot);nt+=ot+ot,B=t[E]("div"),B.innerHTML=nt,t.body.appendChild(nt=B),B=new Uint8Array(881856);for(C in J)for(J[C]+=J[C]+$[C],y=0,m=0;m<J[C].length;)for(N=J[C][o](m++)-(2==C?64:32),T=.00499*I.pow(2,(N%24+(2==C?0:27))/12),Y=[15,9,9][C],L=0;L<4593*[1,3,2,4][~~(N/24)];)B[y++]=(B[y]||127)+(L++*T%2<1?Y:-Y),Y*=.9999;J=c(B),J.play(),J.loop=1,d(0),t[i](r,u),t[i](G,l)}())})}();

149
public/css/background.css Normal file
View file

@ -0,0 +1,149 @@
/* https://codepen.io/mohaiman/pen/MQqMyo */
*{
margin: 0px;
padding: 0px;
}
body{
font-family: 'Exo', sans-serif;
}
.context {
width: 100%;
position: absolute;
top:50vh;
}
.context h1{
text-align: center;
color: #fff;
font-size: 50px;
}
.area{
background: rgb(59,44,207);
background: -webkit-linear-gradient(90deg, rgba(59,44,207,1) 0%, rgba(140,41,151,1) 100%);
width: 100%;
height:100vh;
}
.circles{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.circles li{
position: absolute;
display: block;
list-style: none;
width: 20px;
height: 20px;
background: rgba(255, 255, 255, 0.2);
animation: animate 25s linear infinite;
bottom: -150px;
}
.circles li:nth-child(1){
left: 25%;
width: 80px;
height: 80px;
animation-delay: 0s;
}
.circles li:nth-child(2){
left: 10%;
width: 20px;
height: 20px;
animation-delay: 2s;
animation-duration: 12s;
}
.circles li:nth-child(3){
left: 70%;
width: 20px;
height: 20px;
animation-delay: 4s;
}
.circles li:nth-child(4){
left: 40%;
width: 60px;
height: 60px;
animation-delay: 0s;
animation-duration: 18s;
}
.circles li:nth-child(5){
left: 65%;
width: 20px;
height: 20px;
animation-delay: 0s;
}
.circles li:nth-child(6){
left: 75%;
width: 110px;
height: 110px;
animation-delay: 3s;
}
.circles li:nth-child(7){
left: 35%;
width: 150px;
height: 150px;
animation-delay: 7s;
}
.circles li:nth-child(8){
left: 50%;
width: 25px;
height: 25px;
animation-delay: 15s;
animation-duration: 45s;
}
.circles li:nth-child(9){
left: 20%;
width: 15px;
height: 15px;
animation-delay: 2s;
animation-duration: 35s;
}
.circles li:nth-child(10){
left: 85%;
width: 150px;
height: 150px;
animation-delay: 0s;
animation-duration: 11s;
}
@keyframes animate {
0%{
transform: translateY(0) rotate(0deg);
opacity: 1;
border-radius: 0;
}
100%{
transform: translateY(-1000px) rotate(720deg);
opacity: 0;
border-radius: 50%;
}
}

View file

@ -4,5 +4,6 @@
"3": "Pokud mě chcete podporovat můžete mi dát peníze na Paypalu na dně stránky", "3": "Pokud mě chcete podporovat můžete mi dát peníze na Paypalu na dně stránky",
"4": "Víte, že tento web je open source?", "4": "Víte, že tento web je open source?",
"5": "Víte, že touto stránkou můžete stáhnout videa z dalším stránkám než Youtubu?", "5": "Víte, že touto stránkou můžete stáhnout videa z dalším stránkám než Youtubu?",
"6": "Můžete najet myší na video abyste viděli náhled!" "6": "Můžete najet myší na video abyste viděli náhled!",
"7": "Pokud chcete stáhnout výsledky vyhledávání, můžete použít \"ytsearch:něco\""
} }

View file

@ -4,5 +4,6 @@
"3": "If you want to support me you can donate through my paypal at the bottom of the page", "3": "If you want to support me you can donate through my paypal at the bottom of the page",
"4": "Did you know this website is open source?", "4": "Did you know this website is open source?",
"5": "Did you know this website can download from other website than youtube?", "5": "Did you know this website can download from other website than youtube?",
"6": "You can mouse hover a video to see a preview of it!" "6": "You can mouse hover a video to see a preview of it!",
"7": "Wenn sie das Ergebnis von den Download finden wollen, sie können es mit \"ytsearch:machen\""
} }

View file

@ -4,5 +4,6 @@
"3": "If you want to support me you can donate to my Paypal at the bottom of this page", "3": "If you want to support me you can donate to my Paypal at the bottom of this page",
"4": "Did you know this website is open source?", "4": "Did you know this website is open source?",
"5": "Did you know this website can download from websites other than Youtube?", "5": "Did you know this website can download from websites other than Youtube?",
"6": "You can hover your mouse cursor over a video to see a preview of it!" "6": "You can hover your mouse cursor over a video to see a preview of it!",
"7": "If you want to download the result of a youtube search you can do so with ytsearch:something"
} }

View file

@ -4,5 +4,6 @@
"3": "If you want to support me you can donate to my Paypal at the bottom of this page", "3": "If you want to support me you can donate to my Paypal at the bottom of this page",
"4": "Did you know this website is open source?", "4": "Did you know this website is open source?",
"5": "Did you know this website can download from websites other than Youtube?", "5": "Did you know this website can download from websites other than Youtube?",
"6": "You can hover your mouse cursor over a video to see a preview of it!" "6": "You can hover your mouse cursor over a video to see a preview of it!",
"7": "If you want to download the result of a youtube search you can do so with ytsearch:something"
} }

View file

@ -4,5 +4,6 @@
"3": "Si vous voulez me supporter vous pouvez me faire une donation avec paypal en bas de la page.", "3": "Si vous voulez me supporter vous pouvez me faire une donation avec paypal en bas de la page.",
"4": "Le saviez-vous? Le site est open source!", "4": "Le saviez-vous? Le site est open source!",
"5": "Le saviez-vous? Vous pouvez télécharger des vidéos sur d'autre site que youtube.", "5": "Le saviez-vous? Vous pouvez télécharger des vidéos sur d'autre site que youtube.",
"6": "Vous pouvez passer la souris sur une vidéo pour en voir un aperçu!" "6": "Vous pouvez passer la souris sur une vidéo pour en voir un aperçu!",
"7": "Si vous voulez télécharger le résulta d'une recherche youtube vous pouvez le faire avec \"ytsearch:quelque-chose\""
} }

View file

@ -4,5 +4,6 @@
"3": "Ja tu vēlies atbalstīt manu projektu, ir iespēja ziedot caur paypal mājaslapas apakšā.", "3": "Ja tu vēlies atbalstīt manu projektu, ir iespēja ziedot caur paypal mājaslapas apakšā.",
"4": "Vai tu zināji, ka šī mājaslapa ir balstīta uz atvērto kodu?", "4": "Vai tu zināji, ka šī mājaslapa ir balstīta uz atvērto kodu?",
"5": "Vai tu zināji, ka šī mājaslapa spēj lejupielādēt arī no citiem video servisiem, ne tikai youtube?", "5": "Vai tu zināji, ka šī mājaslapa spēj lejupielādēt arī no citiem video servisiem, ne tikai youtube?",
"6": "Novieto kursoru uz video lai redzētu īsu priekšskatījumu!" "6": "Novieto kursoru uz video lai redzētu īsu priekšskatījumu!",
"7": "Ja tu vēlies lejupielādēt kādu klipu caur youtube meklētāju vari to darīt rakstot \"ytsearch:atslēgvārds\""
} }

View file

@ -13,11 +13,25 @@
<link rel="icon" href="/asset/favicon.ico" type="image/x-icon"/> <link rel="icon" href="/asset/favicon.ico" type="image/x-icon"/>
<link rel="shortcut icon" href="asset/favicon.ico" type="image/x-icon"/> <link rel="shortcut icon" href="asset/favicon.ico" type="image/x-icon"/>
<link rel="stylesheet" type="text/css" href="css/index.css"> <link rel="stylesheet" type="text/css" href="css/index.css">
<link rel="stylesheet" type="text/css" href="css/background.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css">
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script> <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
<title>{{ antl.formatMessage('messages.title') }} v{{ version }}</title> <title>{{ antl.formatMessage('messages.title') }} v{{ version }}</title>
</head> </head>
<body class="gradientBG has-text-light"> <body class="gradientBG area has-text-light">
<ul class="circles">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<header> <header>
<section class="section" id="announcement"> <section class="section" id="announcement">
<div class="container"> <div class="container">
@ -33,7 +47,7 @@
@endif @endif
<p class="subtitle">{{ announcement }}</p> <p class="subtitle">{{ announcement }}</p>
</div> </div>
</div> </div>
@endif @endif
</div> </div>
</section> </section>
@ -42,29 +56,29 @@
<section class="section has-text-centered"> <section class="section has-text-centered">
<div class="container "> <div class="container ">
<div class="downloader form"> <div class="downloader form">
<h1 class="title has-text-light">{{ antl.formatMessage('messages.title') }} v{{ version }}</h1> <h1 class="title has-text-light"><a style="color: white" href="/">{{ antl.formatMessage('messages.title') }} v{{ version }}</a></h1>
<form name="download-form" method="POST" action="/"> <form name="download-form" method="POST" action="/">
{{ csrfField() }} {{ csrfField() }}
<div class="field is-horizontal"> <div class="field ">
<div class="field-body"> <div class="field-body is-horizontal">
<div class="field is-horizontal"> <div class="field is-horizontal">
<div class="control"> <div class="control">
<label class="radio" for="small"> <label class="radio" for="small">
<input class="radio" type="radio" name="quality" id="small" value="small"> <input class="radio" type="radio" name="quality" id="small" value="small">
{{ antl.formatMessage('messages.LQ') }} {{ antl.formatMessage('messages.LQ') }}
</label> </label>
<label class="radio" for="high"> <label class="radio" for="high">
<input class="radio" type="radio" name="quality" id="high" value="high" checked> <input class="radio" type="radio" name="quality" id="high" value="high" checked>
{{ antl.formatMessage('messages.HQ') }} {{ antl.formatMessage('messages.HQ') }}
</label> </label>
<label class="checkbox" for="alt"> <label class="checkbox" for="alt">
<input class="checkbox" type="checkbox" name="alt" id="alt" title="Use this if download dosen't work"> <input class="checkbox" type="checkbox" name="alt" id="alt" title="Use this if download dosen't work">
{{ antl.formatMessage('messages.altDL') }} {{ antl.formatMessage('messages.altDL') }}
</label> </label>
<label class="checkbox" for="feed"> <label class="checkbox" for="feed">
<input class="checkbox" type="checkbox" name="feed" id="feed" title="Use this if you don't want the video you are downloading to be public"> <input class="checkbox" type="checkbox" name="feed" id="feed" title="Use this if you don't want the video you are downloading to be public">
{{ antl.formatMessage('messages.feed') }} {{ antl.formatMessage('messages.feed') }}
@ -72,8 +86,8 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="field-body"> <div class="field-body">
<div class="field is-expanded"> <div class="field is-expanded">
<div class="field has-addons"> <div class="field has-addons">
@ -86,15 +100,18 @@
</div> </div>
</div> </div>
</div> </div>
<div class="field has-addon"> <div class="field has-addon">
<div class="control"> <div class="control">
</div> </div>
<div class="control"> <div class="control">
</div> </div>
</div> </div>
<div id="progression"></div>
<br>
<div class="field is-horizontal"> <div class="field is-horizontal">
<div class="field-body"> <div class="field-body">
<div class="field is-horizontal"> <div class="field is-horizontal">
@ -103,12 +120,12 @@
<input class="radio" type="radio" name="format" value="mp4" id="mp4" checked> <input class="radio" type="radio" name="format" value="mp4" id="mp4" checked>
Video? Video?
</label> </label>
<label class="radio" for="mp3"> <label class="radio" for="mp3">
<input class="radio" type="radio" name="format" value="mp3" id="mp3"> <input class="radio" type="radio" name="format" value="mp3" id="mp3">
MP3? MP3?
</label> </label>
<label class="radio" for="flac"> <label class="radio" for="flac">
<input class="radio" type="radio" name="format" value="flac" id="flac"> <input class="radio" type="radio" name="format" value="flac" id="flac">
FLAC? FLAC?
@ -116,11 +133,12 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</form> </form>
<div class="contaianer"> <div class="container">
<div id="msg"></div> <div id="msg"></div>
@if(error) @if(error)
<div class="notification is-danger fadein" id="error"> <div class="notification is-danger fadein" id="error">
<button class="delete" onclick="fadeout('error')"></button> <button class="delete" onclick="fadeout('error')"></button>
@ -133,7 +151,7 @@
</section> </section>
@if(file != "") @if(file != "")
<p class="title has-text-light has-text-centered">{{ antl.formatMessage('messages.recentFeed') }}</p> <p class="title has-text-light has-text-centered">{{ antl.formatMessage('messages.recentFeed') }}</p>
<section class="section"> <section class="section">
<div class="columns is-vcentered is-multiline fadein"> <div class="columns is-vcentered is-multiline fadein">
@each(file in file) @each(file in file)
@ -152,7 +170,7 @@
<p class="control"> <p class="control">
<a class="button is-link is-rounded" href="{{ file.location }}" download>{{ antl.formatMessage('messages.recentDownload') }}<i class="fas fa-fw fa-file-download" aria-hidden="true"></i></a> <a class="button is-link is-rounded" href="{{ file.location }}" download>{{ antl.formatMessage('messages.recentDownload') }}<i class="fas fa-fw fa-file-download" aria-hidden="true"></i></a>
</p> </p>
<p class="control"> <p class="control">
<button class="button is-link is-rounded" onclick="toClipboard('https:\/\/namejeff.xyz\/{{ file.location }}')">{{ antl.formatMessage('messages.recentCopy') }}<i class="fas fa-fw fa-clipboard" aria-hidden="true"></i></button> <button class="button is-link is-rounded" onclick="toClipboard('https:\/\/namejeff.xyz\/{{ file.location }}')">{{ antl.formatMessage('messages.recentCopy') }}<i class="fas fa-fw fa-clipboard" aria-hidden="true"></i></button>
</p> </p>
</div> </div>
@ -185,29 +203,39 @@
@endif @endif
<footer class="footer has-background-grey-dark has-text-light has-text-centered"> <footer class="footer has-background-grey-dark has-text-light has-text-centered">
<p>{{ antl.formatMessage('messages.footer') }}</p> <p>{{ antl.formatMessage('messages.footer') }}</p>
@if(antl._locale == 'ar') @if(antl._locale == 'ar')
<bdi><p>{{ antl.formatMessage('messages.footer2p1') }} <a href="https://github.com/rg3/youtube-dl/">youtube-dl</a> - {{ antl.formatMessage('messages.footer2p2') }} <a href="https://discordapp.com/oauth2/authorize?client_id=377563711927484418&scope=bot&permissions=0">Haha yes</a> & <a href="https://twitter.com/ExplosmR">ExplosmRCG twitter bot</a> - {{ antl.formatMessage('messages.footer2p3') }}: {{ viewCounter }} - {{ antl.formatMessage('messages.footer2p4') }} <a href="https://discord.gg/cNRh5JQ">Supositware#1616</a> {{ antl.formatMessage('messages.footer2p5') }} </bdi></p> <bdi><p>{{ antl.formatMessage('messages.footer2p1') }} <a href="https://github.com/rg3/youtube-dl/">youtube-dl</a> - {{ antl.formatMessage('messages.footer2p2') }} <a href="https://discordapp.com/oauth2/authorize?client_id=377563711927484418&scope=bot&permissions=0">Haha yes</a> & <a href="https://twitter.com/ExplosmR">ExplosmRCG twitter bot</a> - {{ antl.formatMessage('messages.footer2p3') }}: {{ viewCounter }} - {{ antl.formatMessage('messages.footer2p4') }} <a href="https://discord.gg/cNRh5JQ">Supositware#1616</a> {{ antl.formatMessage('messages.footer2p5') }} </bdi></p>
<bdi><p>{{ antl.formatMessage('messages.footer3p1') }} <a href="https://www.paypal.me/supositware">Paypal</a> {{ antl.formatMessage('messages.footer3p2') }} <a href="https://basicattentiontoken.org/">BAT</a> {{ antl.formatMessage('messages.footer3p3') }} <a href="https://brave.com/nam120">Brave Browser </a> </bdi> <bdi><p>{{ antl.formatMessage('messages.footer3p1') }} <a href="https://www.paypal.me/supositware">Paypal</a> {{ antl.formatMessage('messages.footer3p2') }} <a href="https://basicattentiontoken.org/">BAT</a> {{ antl.formatMessage('messages.footer3p3') }} <a href="https://brave.com/nam120">Brave Browser </a> </bdi>
@else @else
<p>{{ antl.formatMessage('messages.footer2p1') }} <a href="https://github.com/rg3/youtube-dl/">youtube-dl</a> - {{ antl.formatMessage('messages.footer2p2') }} <a href="https://discordapp.com/oauth2/authorize?client_id=377563711927484418&scope=bot&permissions=0">Haha yes</a> & <a href="https://twitter.com/ExplosmR">ExplosmRCG twitter bot</a> - {{ antl.formatMessage('messages.footer2p3') }}: {{ viewCounter }} - {{ antl.formatMessage('messages.footer2p4') }} <a href="https://discord.gg/cNRh5JQ">Supositware#1616</a> {{ antl.formatMessage('messages.footer2p5') }}</p> <p>{{ antl.formatMessage('messages.footer2p1') }} <a href="https://github.com/rg3/youtube-dl/">youtube-dl</a> - {{ antl.formatMessage('messages.footer2p2') }} <a href="https://discordapp.com/oauth2/authorize?client_id=377563711927484418&scope=bot&permissions=0">Haha yes</a> & <a href="https://twitter.com/ExplosmR">ExplosmRCG twitter bot</a> - {{ antl.formatMessage('messages.footer2p3') }}: {{ viewCounter }} - {{ antl.formatMessage('messages.footer2p4') }} <a href="https://discord.gg/cNRh5JQ">Supositware#1616</a> {{ antl.formatMessage('messages.footer2p5') }}</p>
<p>{{ antl.formatMessage('messages.footer3p1') }} <a href="https://www.paypal.me/supositware">Paypal</a> {{ antl.formatMessage('messages.footer3p2') }} <a href="https://basicattentiontoken.org/">BAT</a> {{ antl.formatMessage('messages.footer3p3') }} <a href="https://brave.com/nam120">Brave Browser </a> <p>{{ antl.formatMessage('messages.footer3p1') }} <a href="https://www.paypal.me/supositware">Paypal</a> {{ antl.formatMessage('messages.footer3p2') }} <a href="https://basicattentiontoken.org/">BAT</a> {{ antl.formatMessage('messages.footer3p3') }} <a href="https://brave.com/nam120">Brave Browser </a>
@endif @endif
<p><a href="legacy">{{ antl.formatMessage('messages.footer4') }}</a></p> <p><a href="legacy">{{ antl.formatMessage('messages.footer4') }}</a></p>
</footer> </footer>
@if(month == '11') @if(month == '11')
<script src="JS/snow.js"></script> <script src="JS/snow.js"></script>
@endif @endif
<script src="JS/tis.min.js"></script>
</body> </body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.4.4/polyfill.min.js"></script>
<script src="https://unpkg.com/@adonisjs/websocket-client"></script>
<script> <script>
const ws = adonis.Ws();
function submitDownload() { function submitDownload() {
let frm = document.getElementsByName('download-form')[0];
frm.submit();
document.getElementById('msg').innerHTML = '<div class="notification is-success fadein" id="notif"></button>{{ antl.formatMessage('messages.dlStart') }}</div>'; document.getElementById('msg').innerHTML = '<div class="notification is-success fadein" id="notif"></button>{{ antl.formatMessage('messages.dlStart') }}</div>';
setTimeout(() => { setTimeout(() => {
fadeout('notif') fadeout('notif')
}, 2000); }, 2000);
frm.reset();
return false; FD = new FormData(document.getElementsByName('download-form')[0]);
fetch('/', {
method: 'POST',
body: FD
});
document.getElementById('progression').innerHTML = '<progress class="progress is-primary" max="100" id="progress">0%</progress>'
} }
function fadeout(id) { function fadeout(id) {
@ -224,14 +252,15 @@
console.error(err); console.error(err);
document.getElementById('msg').innerHTML = '<div class="notification is-error fadein" id="notif">{{ antl.formatMessage('messages.errorCopy') }}</div>'; document.getElementById('msg').innerHTML = '<div class="notification is-error fadein" id="notif">{{ antl.formatMessage('messages.errorCopy') }}</div>';
setTimeout(() => { setTimeout(() => {
fadeout('notif') fadeout('notif')
}, 2000); }, 5000);
}); });
document.getElementById('msg').innerHTML = '<div class="notification is-success fadein" id="notif">{{ antl.formatMessage('messages.successCopy') }}</div>'; document.getElementById('msg').innerHTML = '<div class="notification is-success fadein" id="notif">{{ antl.formatMessage('messages.successCopy') }}</div>';
setTimeout(() => { setTimeout(() => {
fadeout('notif') fadeout('notif')
}, 2000); }, 5000);
} }
// If alt download block other settings since they don't work anyway // If alt download block other settings since they don't work anyway
document.getElementById('alt').onclick = function() { document.getElementById('alt').onclick = function() {
if(document.getElementById('alt').checked) { if(document.getElementById('alt').checked) {
@ -251,7 +280,7 @@
} }
} }
// If user press enter do samething as if pressing the button // If user press enter do same thing as if pressing the button
let input = document.getElementById("URL"); let input = document.getElementById("URL");
input.addEventListener("keyup", function(event) { input.addEventListener("keyup", function(event) {
if (event.keyCode === 13) { if (event.keyCode === 13) {
@ -259,5 +288,39 @@
document.getElementById("button").click(); document.getElementById("button").click();
} }
}); });
ws.connect();
let channel = ws.subscribe('progress')
ws.on('open', () => {
console.log('is open');
});
ws.on('close', () => {
console.log('is closed');
});
channel.on('progress', (message) => {
document.getElementById('progress').innerHTML = message + '%';
document.getElementById('progress').value = message;
});
channel.on('error', (message) => {
console.log(message);
document.getElementById('msg').innerHTML = '<div class="notification is-danger fadein" id="error">' + message + '</div>';
setTimeout(() => {
fadeout('error')
}, 5000);
});
channel.on('end', (message) => {
link = document.createElement("a");
link.setAttribute("href", message); //replace "file" with link to file you want to download
link.setAttribute("download", message);// replace "file" here too
link.click(); //virtually click <a> element to initiate download
document.getElementById('progression').innerHTML = '';
});
</script> </script>
</html> </html>

View file

@ -20,5 +20,6 @@
const { Ignitor } = require('@adonisjs/ignitor') const { Ignitor } = require('@adonisjs/ignitor')
new Ignitor(require('@adonisjs/fold')) new Ignitor(require('@adonisjs/fold'))
.appRoot(__dirname) .appRoot(__dirname)
.wsServer()
.fireHttpServer() .fireHttpServer()
.catch(console.error) .catch(console.error)

View file

@ -21,6 +21,7 @@ const providers = [
'@adonisjs/auth/providers/AuthProvider', '@adonisjs/auth/providers/AuthProvider',
'@adonisjs/validator/providers/ValidatorProvider', '@adonisjs/validator/providers/ValidatorProvider',
'@adonisjs/antl/providers/AntlProvider', '@adonisjs/antl/providers/AntlProvider',
'@adonisjs/websocket/providers/WsProvider',
] ]
/* /*

18
start/socket.js Normal file
View file

@ -0,0 +1,18 @@
'use strict'
/*
|--------------------------------------------------------------------------
| Websocket
|--------------------------------------------------------------------------
|
| This file is used to register websocket channels and start the Ws server.
| Learn more about same in the official documentation.
| https://adonisjs.com/docs/websocket
|
| For middleware, do check `wsKernel.js` file.
|
*/
const Ws = use('Ws')
Ws.channel('progress', 'ProgressController');

39
start/wsKernel.js Normal file
View file

@ -0,0 +1,39 @@
'use strict'
const Ws = use('Ws')
/*
|--------------------------------------------------------------------------
| Global middleware
|--------------------------------------------------------------------------
|
| Global middleware are executed on each Websocket channel subscription.
|
*/
const globalMiddleware = [
]
/*
|--------------------------------------------------------------------------
| Named middleware
|--------------------------------------------------------------------------
|
| Named middleware are defined as key/value pairs. Later you can use the
| keys to run selected middleware on a given channel.
|
| // define
| {
| auth: 'Adonis/Middleware/Auth'
| }
|
| // use
| Ws.channel('chat', 'ChatController').middleware(['auth'])
*/
const namedMiddleware = {
}
Ws
.registerGlobal(globalMiddleware)
.registerNamed(namedMiddleware)