From 5c236676a5befcec1c7c78efc3bf293db227ea2d Mon Sep 17 00:00:00 2001 From: Ben Date: Mon, 24 Dec 2018 23:41:14 +0000 Subject: [PATCH] Working version 1.1 - Cosmetic updates to come and client downloads --- public/index.html | 6 +++-- public/index.js | 42 +++++++++++++++++++++++++----- public/resources/YouTube Logo.png | Bin 0 -> 29350 bytes public/style.css | 7 ++++- src/index.js | 2 +- src/server.js | 5 ++-- src/youtubehelper.js | 38 ++++++++++++++++++++++++++- 7 files changed, 87 insertions(+), 13 deletions(-) create mode 100644 public/resources/YouTube Logo.png diff --git a/public/index.html b/public/index.html index a9b9f7d..1096980 100644 --- a/public/index.html +++ b/public/index.html @@ -3,7 +3,7 @@ - Page Title + Ben's YouTube Downloader @@ -21,7 +21,9 @@ -
+
+
+
diff --git a/public/index.js b/public/index.js index b311a23..14f337a 100644 --- a/public/index.js +++ b/public/index.js @@ -27,20 +27,20 @@ let VideoPreview = []; function renderPreview() { if (isDownloading) return; - document.getElementById('VideoPreview').innerText = ''; + document.getElementById('VideoBox').innerText = ''; for (const [key, value] of Object.entries(VideoPreview)) { if (document.getElementById(key) == null) { if (!value.found) { - document.getElementById('VideoPreview').innerHTML += `
${key}: Video not found
`; + document.getElementById('VideoBox').innerHTML += `
${key}: Video not found
`; } else { - document.getElementById('VideoPreview').innerHTML += `
${key}: ${value.title}
`; + document.getElementById('VideoBox').innerHTML += `
${key}: ${value.title}
`; } } } } function clearPreview() { - document.getElementById('VideoPreview').innerText = ''; + document.getElementById('VideoBox').innerText = ''; } socket.on('video-preview', async (data) => { @@ -56,10 +56,40 @@ socket.on('video-preview', async (data) => { document.getElementById('Download').addEventListener('click', async (event) => { if (isDownloading) return; socket.emit('download', document.getElementById('VideosToRecord').value.split('\n')); - document.getElementById('VideoPreview').innerText = '\nDownloading...'; - document.getElementById('VideosToRecord').value = null; + document.getElementById('VideoBox').innerText = 'Downloading...'; + // document.getElementById('VideosToRecord').value = null; isDownloading = true; console.log('Asked server for download...'); }); +let downloads = []; +let downloadCount = 0; +let completedDownloads = 0; +function renderDownloads() { + document.getElementById('VideoBox').innerText = ''; + for (const [key, value] of Object.entries(downloads)) { + document.getElementById('VideoBox').innerHTML += `
${value.title}: ${value.percent}
`; + } +} + +socket.on('download-count', async (data) => { + downloadCount = data.num; +}); + +socket.on('download-done', async(data) => { + completedDownloads++; + + downloads[data.video] = {title: data.title, percent: 'Complete!'}; + renderDownloads(); + if (completedDownloads == downloadCount) { + completedDownloads = 0; downloadCount = 0; + isDownloading = false; + downloads = []; + } +}); + +socket.on('download-progress', async (data) => { + downloads[data.video] = data; + renderDownloads(); +}); diff --git a/public/resources/YouTube Logo.png b/public/resources/YouTube Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f88fdb22f91d5d0416daacf5c293e1226b2cb050 GIT binary patch literal 29350 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

g(~R07)lXAV5o)RQqM(V*uB?bO#*Lg1 zs(N#1CDy)4E&bw@+YUyv^PTjrm_s$6#Sh5=l)i6hm<#=123G3R`1o66&!2~6R=#o! z)dkc&D$*NzF?O&~KtxnD162qh$k1qjf=GBRA@@z*V$b|rb1`<`Kk0ha9rKK}L?0}f z6cE2ZSAb2n2VLh9hl|HUZ1B5=?)0#%a%fG^PXF!O`Z$Sez(%=_*FTZq^|7-0m4`bU z)gjV>?I0&7Oiank?L@xuoHz`5+PuA~rQ#U7Y##gmpBLW0D`uepCe2MCj*-y&c#L|< zllMBwdGRLxgKY?Gw{b7C}EQcEwIe7nTa% zH)xsGtM0!-4~e+F%d03gF3~c8i_Moho5DTyP6)Ojz-T9#d~4@hd4}rQj33pdsg0+Z zOs({eWCb8`Sm3{Km6bzr(VACICAlE7ZgVkV>~i*o5h^MHt~gR91R#2H&_m+AF=z>q z8S8Y0Qe7(e-=Mh46bSy;s!ZNlDCxY6`HKXr6{vGHFVkZ*@bTl z(AP?uSc*(Z&WjezsQ@kQ}M{%d|jI7czi`|1_eP?p)@BuOlQW5g?7TjPA<;IJjd zHAdFho5V3{y(pB@`TpIs?X7B@ImXjd5BkoOb)TKx-QJ-gYeVuFaN*oyVw+KM-6JFU z@@!tWJ7stCZoa#x^Wbee+9AnojXxWzaIwq@IdCo{RnkpKU3MF5?7MuY=}}RsbR2*n zw1-lcBb9P=c+jy*VomlY&frt2wJ-o(>h`Aj*9pq}=H}e#?X>lAmuQcj2t420tC+T% zvSvaKS*s&CfFz@M==mDmmxa&o2`tt!T99*5y6q}2Z`t&eEF38SterVQUe?x1fR~wB zS<9wBFG0e7*hqEgS%Vk9nU$47r_5%GKWW|s@Y02OzI#|t+RvbWtbOGO{{=`J$9F7{ zCz|mbX7*buCV)qj<`(qm-pfsA+}3_+81_TlWn{haR@&WIY#XXA2KB-lcFFb6_>DUy zw&R2Cs{g=^p9mCeY&}g#BjMJ_Btv4=SgcN7obyw-Z|bwGkwVzRM4;pD8yVRRU)NiL zw3O%XVQOlsR@USa`I;Dh^IgQK@2V=gv)%Re*N2P?jjlEdKu1>Ux9cvmJrxafm=YQy ziD6;KWR^eE1LLH=+d%mrlf4dcJXrZgY|K}ypnByMWYCh0y$ek?73_X%U3pUF*iBPcbJyn&A6J9Xs&H%GaMrQwVLkA>|*aZWeA? z9zfgUw*xL=Mk?J_-%KoXG`b03Ev` zT|<|ZHt+=i{&wrfSQ#0=2h(%fN^B=*;zG}gee>BL`}y-BX-2HB3#j^H*}cmNnYg$( z2ARztE_g8#<@mASWE+Rt?U@uR1(h=B%29N)F=T`1AwvWG{adP3ccp}<5G@_u{F%C~ z=diR|Mlslyc5X@_&6+c;EP4jcJ!jyFkX=pO+G`-M*D0~@M8q_gDkiiQ`SK0J^p*bYdCE`d!Z2dxWS6^3^DeQS*v z0L@bF3*VOl$e;&BQ`PIkQHK-Gbb9S%0|}H$<((o&2yXt`t@lvWSt=U5Yh7M-&nA4< zqlcceHHnED5<&Rud%Gcn%br+K8*G8^ot=PvcB0f+a3?2A!fumjapa}YP>Tl$sB5K{ zrc|J0et&oC3@H^fS5&pJZhg~L63?Um{?)5jUPg}SGHlPLdHBlSZ4{VV_l^aE+*BYN zbQ7-{V|kmmw2j}PS{VB*ic)<(5Ft_`5qJ4O;zLHTl(b4bMoZPg0D9R=ZWZWr!3ktZ zyk9S(Iz0s}j+ibWBj80efe$)zNp~&PGMEY+Z1sOWy$qlX)84|Mq9g_~_50LkUh71gM%>FN*R?IX1l(P>A zvEd}SvqA45IzTZF__hR6Fu*XhR-~;t8@{9rl+R^F{5?yq=q<)nj#|` zGc$@K(UvJ@8l~&P{b*zM{nm}pR3i*4hpQUPA~9yDoB8{YD?5x#$q7#bdSgG>Ve z1_kr@G!tki-GBc83g$dCUFO;$fvYVG2u*wJLxubIm$#qA^Tyh56|SwOfz-}0mQ`$< z;L%Rl^1Mr01Z4oFLv&62M2Q?MFK(Vk!BeEd)YDa3I$B!dR7*5I1pzv!ufRmE0Ff3H ze3vTakyBMAMVyvoZxkqX{CU%Fqa>|2Nz_JLPcIu(=|j(Mz}i|reta8ccQKf*Mmo7X zfJawtS50k#4aQ}BHhyuiZ@&bQj%APm`Oyr39#mYLzPv8rQ_J9jr_Y>GS5NI38p<)M za>q1>v0B^MxRl!5eWZki?wZ(V=%CsvwE24^*KcJ1N_~j^qv^ zo=TeTN$3_ls*Zj&rS|!26Cs37f4-VcEs(^yn1zHeRGp@{WbtDppM=G zE8N@PpSI}%h_8o0c+|%F2Lvqms*d?7NmO{?YpCNwiUx3%Krev>nHH^4tSPZP=mCXmc!}4py>rS1J<& zL2d$x5@;bE;CiZ!3*H3mdl}kTK1eS5^kwkj0|I~J8N|V(VHa)n%2js$C5lbJ_NlwHqo`9NYfp@mEVu{svuKG^hI9=U{rg12ytn>%oIO zb>9j4o%y`0Wml_04o7v)W0XJ@Ooc|KymN1`uFS zFbNxCyGsMa1PC8JXKMdEKlpAuznw1@}_IyFSTrKdC<)Cj@+#Sa3I zv>Sh-T+G~85!y`5+$1~sBi!Q`I-0t}aQc(4*%6@5ODz#oEw}{_SQ{D|>U0eMb8a0{ zsq3&^Ini)zCr&3%PcXM@hw@1vh{jl{rD`qE`NKq?gJ1&LLy^c(0uA@D!&L?bhB^GO zeX>iMdAEq$pm{uTF1G+*<_onC;A-ouRV525@QwI49Jv^cQ4$p;$jehWw6%Y5+i92d!bC z9SLV`dP}p7I5yH6lvso=HrFM>5sel)xdtJT3uSiL;1ASdI6UZXuS(YGXlsK5X9pZ& z^LAAY$3vPjv#z(V;WmVXf!9Ys-dY%6S2}t|Y4Zn%A9jf?4t^DS7jddB zkJ*%Kl{{Wt6_l*BjsKG$< z`zht?OUmz=T#K{79Ipm^Q%92M&PsfpZ^QC=Sp_GjB0#OykfFsH-d?_ZnQvQs04g{% zGB&oh?f98aFc(xV6t`*X>q{(|=NXhKL53?T3w8$K3Ye3_^<4BvP6R$=Z+uBT+MAo< z;IFmQC8xYO!r9xWnUWlJ)59*&fK$z)@YO>u5d!TtZ)8CM>C8KV>~Dbo8!(oNan@?#Gv= zp!6Y#VPUj7;t<0vybqxb%7iA;uq_dGJNHB7T-3LG2s(pW-->)n?Mq2FEWPE%+i9Q~ zmk@u&@}ONLW8x8Ug&Rv=F_|DebEpvAPkN^6X>Sq2!HRQ`NHR|B$Ln9s{U}a>Za!P~`32#w$a1fUTY#`L|O<74j$U{rgXS z4W=}WXn}>QSMt7&A+Ser?I-D%yqtGSb3UMYa1<#4w;lGQ5IkDdQ-8dHjRdA`Kr-+l z+DR1{`za3hLy;*e(g5}z-~0XUUs>v3=JA-qQ+1|?QYMxadV2}^v+?tw^IEq4G(aIndc!|;;dwz4M%A;VB zbLuvEOqt1VSbzqkx8@y+^0Knj^2_XqMPo4!a6CjfuF6A*KRo<>cv6nCdMz!jcwZV@ zJG=V<0d@K5a^N-0{rGV!D*isShx2$KWF-CfK5}$#4{`}p7V>bN%secTGP|kV(E)ix ziM+y3Fxh)v>KS}0WkjJ+K`Zj*WAmAjb~vv?1UgS==M_H=p}sw@^|@X!LnQY5 z+NZ;5jr_N#6%8%k zpADvOUVvUWGV(;|>(}k`r0D2qAdC98wmvRWh%x zqtMe7FIkd4_lzkhCYd{5pO@53K1cfXf-sU6lVg)0C$4oToyq4`J{U)UkvMYEYs^Iw zDFA-l*aLJp0$iJe6aX^aty_)N&%B?4MveAWhfxg_B#bgJClp7U>!X@K)JF@^kuFkUr|v3-CW7KFC)mI1VU6-$6H}B z#PZKw`8a_NXxMd4cu6B;isYp+<$hz%)A66>qwdap9Y<^#gCLsnCnhGaZ~lDxSqR)$ zXQhoS$Oe6VeOT&o=y-WeY9Bydb@6uq`3~f$yYlkU2AMAUaT&n1LhLKo4vsH-f@)#A ztMEMvN|iD~CEx%e7|O0`#$%E);Z3%5)`UbpnN^u%?rv_x9-J0FY|u2xLNiHDdATlr zWLOmR(6X{vh~JlYCjFi~(O&hQBt3&N0sK_dz8qQ6ph`zc(}YR`>GUI~? z0If6NT=D^2Ca`p2|7Jtm0qsxcd<}>)%#4hMe}!Ezwj{)uU$PEdJO#8`SJw?%N2Sj^ zk%5sqd;XY*C2O+*e55}w2=R4LI(Vi90vw0h{xr4_GL-EOYe6WX@+{UR0j*c{LZthu8rS)x3X*TG!hP^dnzXNyo%ZT)tnJEye021wFbbO@QKz8IbXN8WW z!Ss7yaxN5W@SF4?Qr}KeFcLdNz&K3IJUe-Q%Yl5ia?crujoEJ~Y=RE&zP5Hu&pF7~ zf~?-nU%uE9$%Yylf=k43tnf1@aSh4`umYu-=HTVb$!XnSHii5@Je-S4{jfeR8ql0y zKR2$xKE6#gj+$hT9UGF3uYAa;SXFVgskyh%W%$b@L2y2w45WAv*w1}mN6Dm87hI)S zhX6x^imRh?12akNAKNliI9$&|(t?$YcLk0)5j7~H8Q9$1eAS-}S=y{y@YLzkT#PDe z@j;(U&$#!4nF?|n`urwzNPAqjXgzF=i~krTiN&%y&FV|qLc5EX4-^_fn+QrRn00?ZNYVNEyu>pH&>-5ZuKZ6_2^)F-t+P{o=+1L^?BtD!XMMur;^GA%a!8@F zzke-A>l_qUS&ntuu#LmAH6L)ax3?$6&2H}66&QuzO_)TP&y7-~tmXwlWJzO7W&c#U6x&$r@uNM0icR@h)P%dBHz7Up2hsY|x{h5iv zn=qjE{dDwHTRO|kTr6!(YoBflZuR&jDKO%XWf#LG{zo7l%IOM~!!7T{3P)r*j zBybufZLY}^5}$~tNK#NApzwzoxGVTtcuGt*!kvxMSa0-n@XXfQ7?`!h<8Vy zrtoZfbsE5p@{nSW01bd)D^Sz`qutYNOZr7NG&Yu9GQXX!0kvJ+{eYUg-Sg-(0T4=n zkT8vt%U{9QD0DjI)RK^JzuEZLuP@Cg#1m)F6!ZgQU^!nQ2o%1jX9BYDk>{h{p^Hts zCH$Zf6VsIXAFwW+p|MO%1mg2mXYNN(et;pwrp7?-{wu+aREpxxT!jM*4%H+NvI((W zAx14VFnW8NwlSiA-VF=9Z~tQz6&3MfJ|rRZ$BzeIER{rdFcrwk%4Ti84hsVz<3}Wt z(th^6aX~*E&9>s1-U3OZ$M&MuRCG183a`|sPlqZQ84-pZl<8zH*8F*Tn>TdzeNkS5 zL5BcMzDb=ixc!w41`fYy1hFi|*k4F54#l9r_?r+E@w@FWRAQ13;6gvGn!z{kD`C{+YJx@v^5hzCX_}F19QEx}_tcW^5u- z?^KlFIv3;m=j~dqiK5(YV$X2$oMweCPts`$JxO79M+fF5bqGS(C-4`3#7>KkP;(_d zvxE9(B{HWsC@(M1S`k4RI0)7xCoI)~z-zBVyThd?JczAdG7MQMod{-<>xWpwD#tEPe^&$ZO|E4dxr@)|WE{f6- zuo}9%=YTntUHMQUZX(%tveFUn>#GS+jAV*Dgf9^igbk+4P8Mg!~l)2{&vMCO_yVR<~Ks9^_^S=^!#P9(m3}OipPL^0MEC z)_kzr;KX2iF}C3bYWN?V#mciss--Bn;UcD?ea^X`tF5W|{=&j6k_i7#KUFo82hQrp??1SfUm z`$V6&*DY|_+26sh1blc`sm+FUQYPdyf5la@lI0G&2IXD@P2)Y#Fud6wV-!2L2j4pmL@z25%Qi=ly)mf>GZ? zWe~6#${vF7mZ3_|*H)};Z2Vzn#^TU{C(6cbqDdvuAGOjjqBKZxGJW^@MZGxDEHhIc zhPDA@A;z@=pAZg<6ZJUh73afqw4Wa>V_uK?YI*N4p|dq%VcPLWN@C5M>4D0E4S?Eg zV`_S?w<*y=KMv)^pt4|j_@k3eBzT!ap)_Jo7xd3ty-ZRLHRoY)!CFXaF$Mh`=r}6% z*dE#NEGBxNPHZj4{yc#hGbV1I!J3dXhw9{dGd?{?w9sKyhkbzc0uYh`4Or=6+2HMf z+?<>!ytJbLHN|Uo z3?IcwTl9%O`38pUx!zni(E3Uf@?-@`T@y_qU~`I!F}GM5vE%opJ$=!L%`=x;8Jj=tfdV6ZQ!OFm_v| zcROXcS><4U=R8xjaZCG$$zV(piujKlJqkXsw7CgXQU&HH2OOvE3;wwp0kA z<3S_7`zK#?;%|>3Z*b5ph{C3#JBfts*-{d{S7feE)td zV0VzFl2$0vRk~5|8P!J?D^h7cCR&dWLbV_&u=9d2_wfAYd9Ud2HQkfTAX9!1*rtSX zevPJODT|76BRMHb?M;mFR{mOhHz9X8Be-WX>vohm1_i4qZ3C>osI+uSadaI34zx(E z$-{twyh6xXCZ;Y*I_Yz1O9bvs*kX_xfD??sKau;dTi|IJUMvgrEaj9cX)Phm#0GXa%abmP}gWwDZcusL46{ zCo%=na{3S>GJ(|_Hl7{@F9Tmp^FAM=+2oFUw#7Vvn`HyXVD=bISRHEygjIpzn%$$VCRs=ruP~c$9qGS_q|sz z#Mt-@Jbo*?(=pyut&hqSoEZKjZ{SL;!S%De&bdSGgMlP(E%@YPE7&?Nv3TV5HWU!w zo8B&2Yg_b$CF7`#rSMJibE!km^v?#;Hr|5*!oZ{V7F2#P(lxggLV@2Q!*EDxLoOdm*&*uFCoNm7@VjCr%g({ zJo07X&IL{N0&oftqSZgkd&#=Jn)&v!ts}bF^Wa^$bFWnk(%!6`hcvZblS{w0-aGZB2E$4m~IQJq2q_BuM+7(X&QX*e*fE z1goO){3eITuH#KpnPSbwZ|AtQZ0p6zM8~p(9!Ay&`#)H0QUj=BW>cL)*;dG%a z#2gPC`Bps9&CDu&MP}vY+9x;^n^;_nc05hu=h+bTXHy6K>rCKW^zD;&Pak=1YZz{> z4=zqfK@WF0U?x!qgt4c9z@39(=lEnsO?aiuZLrsXf2V135Cat}hOAg{(2KU2)d34`38+K^_QD8!@DomRzFrywaEpMGvh zcXrD0%dAC|`jm?ZeJZ`H@L}SS?m9(~6pZs=lU?O5XV{20XSD4hDevq$olbOFRi`bn z@1G-0Q4nKwu}}OnMW4zjy*gaa^eWQ@iKs~Vog~=rLN%MP5W7v0kSgqRacBGY7(Z#a zPO(#(qpZ9+`8uUL7cVDbnXSU=%W+zz`>_v&0(X~HPZ7{FUs85*lQOv)1+o)6G@*Rb za>)Jz(@+i!A?>lhW`~Ea%B*Wk@3BdqqHARFG%f$^bdZTNI?L2Csd@4|JT12xns8=a zzl~=;bZ`5SOWpXA?tzx()%8vK7@_-DF2YPSSTIOi&C3>@^zj#J9h!ka z1ixldb_bZP+XlKMDIRA5v#G~3^VP}sgJ?Y{R9$A5XN4?b@!~Fq%R|u*W4$C`&fFG7 zMKwWbDDwx7%TdDkb>DIBdc};4*9TJHIe&B#wDGS?V|GC>mrfnwn)|+9)jSy)aBS8a zB+y@i(a1txyX;x!yV5Wm@;FRSg_$)L499FF4`QS|)_rF6&Z~pZmc0TaonTByfN|>m z!#L}VGKv+oGg4(n@ z#@agJ2v@YLo1z@K;#BB&TUMN|#`!}oT1H3OpFLy*(>h>;F2MU7^C}iDA8&7`n(D`a zOZ&8Gp%{XB?CbmB#EZ<2Yo5|azAXoNoICEga5qCs^ykaAg{fc53T0(Ih-H+X$`REQ z{*i30+}RZ0Oks?$u<(14#dmjLQ|@oTu+)wUY-%vF@Y(76>?)U-hrL+!vxc{@3LG5w z=>t<^2Ppr4!d%|0lFkv45M#{pXV=y?tpbR0G+`QB} zo?y^c8c^Bun@Io|pnH7GLtTf?bX7~#aDp5M>k9^M{JJ!KNYg2$71Kg!`cd;baG;>i zsdT5jp_3JC>aOUt-2YaQw&|ND7wsTx>5g3M*gAJ@dwaPd_JQ{q%6jShy$W6j$5qUe zGJ)h|l{)+)T3j{RTpv=kGxkRmu=3)wGXaQcd}L%K_C@VcK~*hKDqMbd8iGs+*mc-J zd&o)l7f9OS{%agH;6CndCWD+0zxvM!zv&kae}&ZrluWFcM^9(maiAfTe&=#Ef(Na|~+>bWp}h`tK4e!;oz+x!j6P zi;dybDRJi?r98^O9>8cYW|#x!_Oi0%sp;5K*c_c^iurXKZ5V?NJgL`z;%T7(16Vzv z8oPlqYO%1llBhqID9wheH6E(rM#oqb4@hWwNf=!bN&xZ~@)yUMd(ex3ci^X^IEKO# zkX>-c#qj3zsHl^7pHM|yqoSr}#T+t2$1KRz2OKptB4H|l6-V(VAwgkI$gF5UpA1IOXe9dCJ}R*<0nzh5`-ch zO6hCYG69HVc>uzp)31?7b3p%yv4mAo0;nkS}2hV~j2EZ`~d5rQ&Jv^(TVledPtP*ORQi!^m+K~6N zh897dg@|pO;E6zqhX)cul9+LUNJ3PKZ8pAC@dYw`Qn-(Frg7q8WICc1_)Kd{1$U<0W2x{_+|o*T2Mpf@Z&#pjV^i)t>8bVGQ?N^e}CY<=Q47_Y|RS|XXpM@L7ov=6}M4YQ}ezuvih|9EB-NmENd*)e zp3Iv-lw=KTU{@U@9PiNk`A04DVZ z*aE4ksiP~~!NY4v>G;SxLj_8z-YcFlgXgcbh76el)$lz!0f}5|FdgUN+>8eupgEKN)(Q&@d4b8^ zdFo3B3Q2h-A?bqemf!ncM6l*!}bDyr^ohQvlR+RolNiNywAJ;gDq6vEpRH0eKn{wJFj50!K=EsdezIw1XCzT^bADdAwK#?4^?4<`vKghJ~I;6Tt( z9u9B7v>?pL92}2HKP(bhkqzAseOhPMbX&aC$v|~5WAE?pD;s=+(~RnR3UVzwJKjJt z7!ib^p0XYm34ZbgIA)krfP=F`pA{Ir+Xmb_n2i2=33wIrwL3MeaG=1FG_->HG+^ZV z3a+g>0kS@*uy7@KG+>M@4AEi_z8(-IJvHO{!QYMw%#@CBjk{!%maxyi)|F^!HPr;r z?0c9Q#v}DVFKwa_GVI_ujJm9P?r=aIA6;kG-5JlG z?e0pKk0v~VehGk;NgoV#u-aUTh>y=Ybd=!%K?rC97+J#1%gTTzjm|a1SBF6+Ks`_B zV0aiZ0*JdytIyoN#Zu_?meq%&Nmf*}zd^_VMcAz!DWau+YS!k{g(_od@ zT8H3HLoF?i@a9PcvI1C6z_`aM>-*pHqxa}!j(*h03DkY!9vn6V5q^SM7e-|ye>xvtOk zK36}~VP;;h*K>O;xBGp#k7e=V2+F<9`?1hCi~V~QY80G+V2=dR!94&ppM&sO=a8}S z{Ejg#bSpqY@ZQJ@_k&D~^Dn;zDw8mn#Ms73{WH2G0QUnvSdAk0AfjO;YuT%;(0N@1 zWEf!UNK}juFgRR8Q$tTR9yjVT<$WUMW}jJoBZlR%$U*Gv_7)v*)UI`$vM@y~(tBcV6 zFdoq%jG|Py?#j(7RUoYq1w_vNMhH>CXp7JNLmrNfjzRQ5z!s-OI(I&RP~^b{rf-hU z`1x$F_Vl5lA&nwB${z&KB%+{*$Vg3zyI6eCKteVZf3rKyp~wcSF8i##4Cp{6UBC|X zZ#w~!4$QkgKCXG`xgrN9IJEA=isA*w&BaD16P+=rvJWyW6qF48HeWw(#+wn7@c-HN zt_XD(?e~zc)DZdB&o|=KGm<&tjz>s7fCA?MJG&6Wa(KP~!i=n%Kv|j0zaoBrs_bDa z&m%Fw=+Fw`{diH`FDvYjO^UzmdS*a(gZqgS`$;&=#w#Ix3li`ev%uMj7qV_NQ6;i& zh<=KT9^|x##tXVN;IT%WPYRT7{lE+Gn zb@j6hmNU$4p6d<=$Pej0`x&Bz#vigj8RHat8GRPzaxiTWoG;vI;v2k-Fl zU!$P7MF+x;nV4HkB3wXknrsgup@7e0(zV;J-r5$e32Pf1<_)0ySEd}qt`1DAmIx(K z-zirvv3qs&bdapU4`s9~0WWI?ai5HWawL>F!~}o?eki{k8zX|RUQg! zR@g=m>mNjetZhp+^htz#?(avFq3|(0gPJv!swc(;p+p7>Ky-6?DwFw3mI&z?0`ZpY z>IDlH5QTvy6mCF&6cCQyAG;mX#Qc9C`FPhgHt0!fbg@r$pfg9xaX`Oke#`6_0$*yW zm-x9M3IMu>u@B@G)fRw>RJi(8w9rUWhxkjBs9= zsEa|=(4H-}EMgylbAh9^H)4K}aylevPoF=xL=O3Ui!shLjE<$PZ9IS`9+3{!TT|-^ zqp~mwWnPA>ia=yGI`gREyxECM$`eNqg`ZazyQ}#7m9c469}N8ruOCN`A8rQN=F`mX zYq8#c(?=8p9Ri6&I`Mgt?) z3uhc{lh(P-qX zM5FlY{N1+g+j~vPtW^lM$jODB5P=Bkf$!hs+o1PA5_2$0-WX$-lwh9#A`xO%LMfx& z92xoS$4~$!0hw-tgM+N!Om#RXF-4;~Cv)85>YUb9 z6y9+fTeu2nA}9{fc?yctPX`TT#42pm&mT5QQlEAjM46FczxbcrE(G%)%{vHS8QCy+ zJ83ie-zDwJpztUZw9deEfzV&Iu;kUz{SYJGYDlw<&Fo$twq5(SiNe+p3F#*ExgD7P z{I#dIav(=&IF-|uw9k+1Jd91R(hic!Qt$y?yoihU40=gow_JRt(E+%S`;d?cm#HPvh0s(Uz)&1Hw?sX?;{Mn6J35oO2erG2(Vir!1v% zCi<6TjlQ4)9zLx zW+T8hc~8YSw@P>nLr44>9}|`7rsD!@H1vxs5$f)16R0oPi@)R}G(dWHS54ZBg_waL zb(qTUp9u7#5R&q4Tno`954V2%_dj+FaKHd26G9UKO?FyW*a}Yqw_)V$yf2ordHskc zKI4Dv2o<*fS@9*+b)60s)rW*fkBIM=X5OHtTV+4GI*Z;zLi$ce=uHUm^ksc8eSoB* z4-d2sB0vk3 z6?fE%_}@3`q>zBKurMU()0!M2RfWo3C8=B2v7`V9`YC(!z`#5r3C1btOhaQ}FHn+N zSIA+W^-El*iBf6B0V;j*US_`>x%!v0-l!YEuwm? zp}PpuYA$)i*nWqRO{b=o$1P{(VJF6|@jInUoe1f*GtJ=PZFI)o`n{0UnCD5bxmR;f)z}$ zjenhZ5lI?*gdr+aGG(|LRZPo+x-nCH*1zI)er;?qcIr6R`5SXzR-q6Fz{IHeo14!u zfQFb~)pX$qt~Ne24cqBJxu-NLRq&>xeP1VA18wj_++e175M~LPUaK@H#P$Jlb925y z9cw*&qsVxA0?fX8^27EO@Kh6ox{bK7s&+41y!>>*tjgMrl+~OW3#>mtU7$}HUfhOT zRQOoyxgBbgPrZ~e4YIx>RqHG8T<-aQO_`dSIzZ>n$CSWW@cXSxf(|j%>Qr&u=!%^s zp`h@)xp4h}?Lt*m)jr`QrxBAhY3!P=u|y7cM9UiCH&EuA3DaGMC} zoRKtgsc9_7Vw%#vUvjF{24b80PFxJ7v*q<3l#b?&DIhp%EVjjBHHIp4x2-P^#=p zr3#)>raHtKS@wAHDyCMl{7;u_gMQw{<0Y<&oEB=U-3*8s5bq_uIgZ&pSUl-TL&9KI zPN~6R1FozF^I1mUT>%Oja^t%Gt=^YWDzPk*i7M+IVUcwfTQcPgR?)Tl@N!m;^v1s% z71OB7f@a3s-Z`_sQLOFdX?!co81?gS-;#^EA3Rt?6bjFNdxf);-`Yz_Mr;@jxb?J0 z5rnlU6A*WZRrVZ@@&GbBkBZ9olt#RJE?MG{^i-(uc zIwcvqI2jLN(2_MtNH+)!)@&BiT_Kpv7YQf*c=guhs+E~3Lnyb~E(WYd&@S0&aiss`x87cX7tpP#H8d;&-00$97wd&cM_k1EHC6()m zQ`QO*wkywz2cEekw5WI}!mAJar@`NG-gT-6QD*Q89f>Dl+sBg%ATP*1({C=~nycjB zGQ|8`sNVX3{01C94wi*RHoqSf)xb;SupEtTJM`tK*x+@BGL2tXyDI0Me%NKU{PbF~ zOaPGyQ;3Wx{YWfM8-%XBEGEsAoFgAke#Ye@u^WWwgO#_q&5(Y@%CE%{xMyFuif zscApu;o)olz%UENUNZMUNtoLFP$qrKoMN65u@{vtTGIow6&!>0d%lH`v{>0;s(8gx zOhhuZfBhOfBtBMFnl^i@4$&uC+J$FrW)1@pqYXzhKGVm=MSIAZ1fbEVT*Vf{hyv%M zM}IIxQ@y-ICb&6aAq%ZXiiTSExKYDmZIW#*6AG(~Ti6vtI zQD4MEU^L{E2u>*-dA`0|+Vq8zKOSG$!WG;$PN+4})mVO=+O>Tx*S}=a{#0x3c4xI< z^j^uujunq-(|$O~PQH8<(>Leta)hqP2>65kC8;ncO!lCIL;8~G7A+iB7FiOJx6V&d zO~v_tTGvBxeBq)YG(*Ibt$n;qomel@HK#PIvrhFE`oAkY zbN7&8$#SWTJ90|;J z2-q+8`+gK`26MX>wS|mSl0(z)KZX^vOvKC3oE#Nl4wEFU|dmgLy(w~;ub`Q z@!}nxFzU%PGZRsCg^2!P?q6sBG)0(=^IlJO+uuf=V>mxI0JCHhON05!g@hu(Gn5yU zg~3$|9d@EWBL}BV<_Qa%_S>73l^c1DR3UwyquXwu?Oj@s$hjZw5w->rAnL>!t(iJM&q@834EN@!SKAVt6%)Kd z)_wi5F2ek;l$$4vY=QHEd2DGwy9!;ZI+t?^^M`_ph*%a;9RmAgeDFYPo)xo1m`@xN z5JZ4~y7Xq0q4=+h7+ysTjP3pE50Vv!r$*X%4W?%$XViosnkF_jTqOVQ?1nT%Wkw$_ z-G^+qB^k&Tv5fG8$B-N5me1hD+jB{O$uMnT?#$_iUSX`UNrX%ulaWZG%J6!BQE&?my<&KtfZcq}a^!(3PIhIfxI(b2&VsS*1)D&9%Z6kTnC z@y`mydtZVz)+~)a%L!M%6xt)R`FOjYoYTIi;PrVtR-1o&z0)=ABqZ-9Udg5Cmm=l7 z?;n4MIU5Cm`rPUTc)u!7^TAJ*b21yeUIT>BH{|Sw+P<$j+lxV;ETzdW1jdThHxiu z{_NQSPtU=^u~;lFR!`A~P8?kDzQQVp=>qBjB%^O?I&67O5}P3cN!JK5ZCi9bplpPq zr=FDLPEY5Unh-j4BAT3B|*M(9B z=f8}Siui?6bW(6#zds%M0%8Yzs}*q{XTe2iwQRShXQeqQGJ0?hnB zX0TX7Ef4eBR0l1;dK8}UPwcy9Ud+CI7o*DA0PC#P_O!$Q?mZGry~{iO9+cev9n+3sIN4Y z=;>4bobt2BR-t_9yh{Y3Ms*NyJ!!YY&H|&M&1NG@N6Qvo1>OzFJ&dsTVZaf6Ivl#R z6Ga7>U=Y09#Du&E5(iDpAQWZD<9PMz7349FUgSRT4-8};@cnv<8mb-hcxEE}^rWIrSw9v%{N<-$!=9O{CborfqT>g`*c z-`||^UMpPgOol$(VTx~qra;Dpz5w|%b2=xrdDqwtH?<<4=Ccs7)2^M~rA70GNSmY` z1X+NFp~zt(r1(X#3w!icGvx$bX7y?~t-#F(dr(x9eiOB}F)wRE#eITw`_;z7nc=;j zzGVjUlVliG&z>9Qk7}}n zlL?jaoy?kqIp7Lk=!%&^At)7PW1H9;x=t`0TN^wlv7;lP?!->XZ(H76x!lq^Jtf}~ z6<(GPBSJm6s@y5T6)&uinKo`0F4x+oBoL0(D%0f$nw1tVDs#W|*y8oJqp`YmtC`jc zn(E2fSAu|@H#bbzla#gS`eZpbWD$nrPndR$vt+DyCD zeD!5(#gBK6%c9<`Te8GZ?``dHH?qz(Z6VPItRy*I@ns_qK<~+^{C>`L-SUVr$-(|8 z2fBSw_pNce*8;&%PmNV{?f37^51ZnIz1%Smm)wfH8EWf`)vpGadT!e^>kI%>%7u{& z8(72Th|i)M(*7H0L+FzIKI)>pS26~QVZNTLwbdWQxo8#g&DE_JjmtBvhbOw!^7Qm- zFU+caq&KwquC&rsx{^|asQBI9Q*hfA^VlO+EEOaF)^SL`bK-l#=avLkxvNSrk45Sxq+F3&_|`o}vRs>!pDfa#=N-Qy7csWm{9I8V`>#26 zZ>}_!@y!}P25Cn%`fD}?b-k)*M^8~wa&@1kM?HO-_|z!nY5xy>Z*Q${aXoj=r%SML z4PqX;V%g+NVQx>@Y+1^&4(Y#%RoKOTd50s(B%>MUgl|~+d(QVhR&U0MDengNjDXv? zmMcrd8xM{DI8*U-s!mkV={j@3*he$KOhnr%p{vY)F(SxcOurVGPfu%CmPtC!U(eVa zSW&m?xueOjqKlD{PSQF45cZSut|wJz=OpJ}=eauTk~A9<+4wBK}^;cs3Z)>gf1G9^(w z-BfLXA>{+-M#UXwuzWSk#-i@(W$P|344oJWglZf^Ln4qsWbSt?Rlc_pQ-Q6nU&dZdE!&g`6{u0$o#P6z5>FqnJ1OgeROfprq zGT=pruFheGvhe3*wU$Kt#?TnaT`C3#vBJu}_(KaO7bA5~8AOaK4? literal 0 HcmV?d00001 diff --git a/public/style.css b/public/style.css index ad6d181..9133357 100644 --- a/public/style.css +++ b/public/style.css @@ -30,7 +30,12 @@ textarea { height: 100px; } -#VideoPreview { +.VideoContainer { + padding: 30px, 0px, 30px, 0px; +} + +#VideoBox { + padding-top: 10px; font-weight: lighter; } diff --git a/src/index.js b/src/index.js index 60ed9f1..c393c27 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ const server = require('./server'); let config = { - serverPort: 80, + serverPort: 8080, downloadLocation: './' }; diff --git a/src/server.js b/src/server.js index a9f476f..e2926c3 100644 --- a/src/server.js +++ b/src/server.js @@ -37,8 +37,9 @@ module.exports.listen = async () => { }); socket.on('download', async (data) => { - logger.log(`Socket id ${socket.id}' is requesting a download`); - youtube.downloadVideos(data, socket, {path: main.config.downloadLocation}); + logger.log(`Socket id '${socket.id}' is requesting a download`); + let toDownload = await youtube.resolveVideos(data); + youtube.downloadVideos(toDownload.data, socket, {path: main.config.downloadLocation}); }); }); } diff --git a/src/youtubehelper.js b/src/youtubehelper.js index a8559aa..55e743f 100644 --- a/src/youtubehelper.js +++ b/src/youtubehelper.js @@ -1,3 +1,6 @@ +const logger = require('./logger'); + +const fs = require('fs'); const ytdl = require('ytdl-core'); module.exports.resolveVideos = async (arr) => { @@ -29,6 +32,39 @@ module.exports.resolveVideos = async (arr) => { module.exports.downloadVideos = async (arr, socket, options) => { let path = options.path ? options.path : './' - + let numOfDownloads = 0; + for (const [key, value] of Object.entries(arr)) { + if (ytdl.validateURL(key)) { + try { + const stream = await ytdl(key, {quality: 'highest'}); + stream.pipe(fs.createWriteStream(`${path}/${value.title}.mp4`)); + + stream.on('response', (res) => { + let totalSize = res.headers['content-length']; + let dataRead = 0; + let lastPercent = 0; + res.on('data', (data) => { + dataRead += data.length; + let percent = Math.floor((dataRead / totalSize) * 100) + '%'; + if (percent != lastPercent) { + socket.emit('download-progress', {video: key, percent: percent, title: value.title}); + } + lastPercent = percent; + }); + res.on('end', () => { + logger.log(`Socket id '${socket.id}' finished downloading ${value.title}`) + socket.emit('download-done', {video: key, title: value.title}); + }); + }); + + logger.log(`Socket id '${socket.id}' is downloading ${value.title}`); + } catch (e) { + logger.log(`Socket id '${socket.id}' failed to download ${value.title}`); + socket.emit('download-done', {video: key, title: value.title}); + } + numOfDownloads++; + } + } + socket.emit('download-count', {num: numOfDownloads}); }