Skip to content

Commit

Permalink
ui changed - renaming/deleting files - download quality added
Browse files Browse the repository at this point in the history
  • Loading branch information
farhang-sa committed Jul 2, 2024
1 parent aab2334 commit ba81425
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 68 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

/dist/yt-dlp.exe
/dist/yt-dlp_linux
*.mp4
*.webm

# dependencies
.idea
/node_modules
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ App itself is easy as it gets! but mixing was good.
## How it works ( for dev ) :
<code>nodemon.js</code> is the answer. <code>npm start</code> launches the <code>nodemon.js</code>
and it will execute an empty <code>fake-server-script.js</code>. ( as we need php server not js server )
after that , it starts a <code>php-built-in-server [php -S localhost:8080 -t ./dist]</code> for serving files. so nodemon watches the files for changes ( to run webpack ) and php servs them.
after that , it starts a <code>php-built-in-server [php -S 0.0.0.0:8080 -t ./dist]</code> for serving files. so nodemon watches the files for changes ( to run webpack ) and php servs them.

## How to use :
1. <code>git clone</code> to your server
2. ### do not npm start! ( it's just for dev )
3. delete dev files and keep the product :
- if server is windows : <code>npm run windows</code>
- if server is linux : <code>npm run linux</code>
4. upload it any where you want and use a REAL php server
4. or just use releases :)
182 changes: 132 additions & 50 deletions development/scripts/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,51 @@ import { createRoot } from "react-dom/client";
import { BsCloudDownloadFill } from "react-icons/bs";
import { BsTrash3Fill } from "react-icons/bs";
import { BsListCheck } from "react-icons/bs";
import { BsPencilFill } from "react-icons/bs";
import { BsFillFolderFill } from "react-icons/bs";
import ReactLoading from 'react-loading';
import $ from 'jquery' ;
import '../styles/app.scss';

window.React = React;
window.$ = $ ;
window.baseName = typeof window.baseName !== "undefined" ? window.baseName : '/' ;

const findExt = ( fileName ) =>{
if( typeof fileName !== 'string' )
return 'mp4' ;
if( fileName.endsWith('.webm' ) )
return 'webm' ;
return 'mp4' ;
}

const findGoodName = ( fileName ) =>{
let filExt = findExt( fileName );
let newName = fileName ;
newName = newName.replaceAll( '.' + filExt , '' );
newName = newName.replaceAll( ' ' , '_' );
newName = newName.replace( /[^a-zA-Z0-9_.-]/g , '' ); // clean name
newName = newName.trim( ' .' );
return newName + '.' + filExt ;
}

const getIcons = () => {
return {
icoTrash : $( 'div.icon-holder-trash' ).html() ,
icoDownload : $( 'div.icon-holder-download' ).html() ,
icoRename : $( 'div.icon-holder-rename' ).html() ,
}
}

const getFileView = ( fileName ) => {
let { icoDownload , icoRename , icoTrash } = getIcons();
let view = '<div class="m-3 text-start">';
view += `<span class='btn btn-md btn-danger' title="delete" onclick="window.askYtdlDelete('${fileName}');">${icoTrash}</span> `;
view += `<span class='btn btn-md btn-info' title="rename" onclick="window.askYtdlRename('${fileName}');">${icoRename}</span> `;
view += `<a class="btn" href='${window.baseName + fileName}' download><span class='btn btn-md btn-primary'>${icoDownload}</span> ${fileName}</a>`;
view += "</div>" ;
return view ;
}

const App = () => {

Expand All @@ -17,7 +57,10 @@ const App = () => {
const winBinLink = 'http://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe' ;
const linBinLink = 'http://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux' ;

window.askDelete = ( fileName ) =>{
window.askYtdlDelete = (fileName ) =>{
let ask = confirm(`Are you sure you want to delete '${fileName}'?` );
if( ! ask )
return ;
setSystemMessage( 'deleting \'' + fileName + '\'' );
setIsDownloading( true );
$.post( window.baseName + "api.php", {
Expand All @@ -28,11 +71,48 @@ const App = () => {
});
}

const askDownload = ( action ) => {
window.askYtdlRename = (fileName ) =>{
let filExt = findExt( fileName );
let newName = findGoodName( fileName );
newName = prompt( 'Enter new name with .extension' , newName );
if( ! newName )
return ;
setSystemMessage( 'renaming \'' + fileName + '\'' );
setIsDownloading( true );
// clean name again!
newName = newName.replaceAll( ' ' , '_' );
newName = newName.replace( /[^a-zA-Z0-9_.-]/g , '' );
if( ! newName.endsWith( filExt ) ){
newName = newName.replaceAll( filExt , '' );
newName = newName + '.' + filExt ;
} if( newName.length === 0 )
newName = findGoodName( fileName );
else if( newName === filExt )
newName = findGoodName( fileName );
else if( newName.startsWith( '.' ) )
newName = findGoodName( fileName );
$.post( window.baseName + "api.php", {
'link' : 'renamefile:' + fileName ,
'to' : newName
} , function( response ) {
let newName = response.replace( 'video renamed:' , '' );
response = getFileView( newName );
setSystemMessage( response );
setIsDownloading( false );
});
}

const askDownload = ( action ) => {
if( action === 'clean' || action === 'clear' ){
let ask = confirm(`Are you sure you want to delete all video files?` );
if( ! ask )
return ;
} // resume :
let link = action ? action : $( 'input.link-input' ).val();
setIsDownloading( true );
$.post( window.baseName + "api.php", {
'link' : link
'link' : link ,
'quality' : $( 'select#quality' ).val()
} , function( response ) {
if( response === 'yt-dlp binary not prepared' ){
response = 'yt-dlp binary files not available ( check internet )';
Expand All @@ -52,15 +132,8 @@ const App = () => {
let html = $( "<div></div>" );
responseValues = JSON.parse( responseValues );
let co = 0 ;
let icoTrash = $( 'div.icon-holder-trash' ).html();
let icoDownload = $( 'div.icon-holder-download' ).html();
for( let video in responseValues ){
let nh = '<div class="mt-1 text-start">' + (++co ) + ". " + video + ' ';
nh += `<a class='btn btn-md btn-primary' href='${video}' download>${icoDownload}</a> `;
nh += `<span class='btn btn-md btn-danger' onclick="window.askDelete('${video}');">${icoTrash}</span><br /> `;
nh += "</div>" ;
html.append( nh );
}
for( let video in responseValues )
html.append( getFileView( video ) );
response = html.html() ;
}
} else { // File Name received !
Expand All @@ -78,41 +151,38 @@ const App = () => {
}

return (
<>
<div className='icon-holder-trash hidden' style={{'display':'none'}}>
<div className="container">
<div className='icon-holder-trash hidden'>
<BsTrash3Fill/>
</div>
<div className='icon-holder-download hidden' style={{'display':'none'}}>
<div className='icon-holder-download hidden'>
<BsCloudDownloadFill/>
</div>
<div className="container">
<header><h4>Welcome To YouTube Downloader</h4></header>
<div className="row mt-5">
<div className="col-12">
<div className='icon-holder-rename hidden'>
<BsPencilFill />
</div>
<div className="row">
<div className="col-12 col-sm-2"></div>
<div className="col-12 col-sm-8">
<header className="col-12">
<h4>Welcome To YouTube Downloader</h4>
</header>
<div className="col-12 mt-5">
<h5>
See this &#160;
<a target="_blank"
href="https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md">link
</a>&#160; for supported sites
<br/><br/>
<BsListCheck/> list all videos - <BsTrash3Fill/> delete all videos
</h5>
</div>
</div>
<div className="row mt-4">
<div className="col-12 col-sm-2"></div>
<div className="col-12 col-sm-8">
<div className="col-12 mt-5">
<span className="input-group">
<span className="input-group-addon p-4">
<BsListCheck
onClick={() => askDownload('list')}
style={iconsStyle}/>
</span>
<span className="input-group-addon p-4">
<BsTrash3Fill
onClick={() => askDownload('clean')}
style={iconsStyle}/>
</span>
<select className="select me-2 ps-2 pe-2" id='quality'>
<option value='360'>Quality 360p</option>
<option value='480' selected="1">Quality 480p</option>
<option value='720'>Quality 720p</option>
<option value='1080'>Quality 1080p</option>
</select>
<input type="text"
className="form-control form-control-lg link-input"
placeholder="Enter link"/>
Expand All @@ -127,25 +197,37 @@ const App = () => {
</span>)}
</span>
</div>
</div>
{systemMessage.length > 0 ? (<div className="row p-4 mt-4 p-0">
<div className="col-12 col-sm-2 p-0"></div>
<div className="col-12 col-sm-8 p-0">
<div className="col-12 mt-5">
<span className="btn btn-success p-3"
title="list all video files" style={{cursor:"pointer"}}
onClick={() => askDownload('list')}>
<BsFillFolderFill style={iconsStyle}/> List All
</span>
<span className="btn btn-danger p-3"
onClick={() => askDownload('clean')}
title="Delete all video files" style={{cursor:"pointer"}}>
<BsTrash3Fill style={iconsStyle}/> Delete All
</span>
</div>
{systemMessage.length > 0 ? (<div className="col-12 mt-4 pt-4">
<div className="alert alert-info system-message">
<h5 className="m-0" dangerouslySetInnerHTML={{__html: systemMessage}}></h5>
</div>
</div>
</div>) : (<div></div>)}
<footer className="mt-5">
<h5>Note : if you are in dev mode and have low speed internet ( like me ) <br />
download yt-dlp binary from <a href={winBinLink} download>windows-link</a>&#160; or &#160;
<a href={linBinLink} download>linux-link</a> and put it beside api
</h5>
<br />
<h5>All rights NOT reserved for <a href='https://github.com/farhang-sa' target="_blank">ME</a> </h5>
</footer>
</div>) : (<div></div>)}
<footer className="col-12 mt-5 pt-2">
<h5 className="alert alert-info">
Note : if you are in dev mode and have low speed internet ( like me )
<br /> download a yt-dlp binary from&#160;
<a className="btn btn-lg btn-primary" href={winBinLink} download>windows-link</a>
&#160; or &#160;
<a className="btn btn-lg btn-primary" href={linBinLink} download>linux-link</a> and put it beside api
</h5>
<br />
<h5>All rights are NOT reserved for <a href='https://github.com/farhang-sa' target="_blank">ME</a> </h5>
</footer>
</div>
</div>
</>
</div>
);
}

Expand Down
13 changes: 13 additions & 0 deletions development/styles/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,17 @@ body{

.hidden{
display: none;
}

.btn {
border-radius: 0;
}

a {
text-decoration: none ;
transition-duration: 2s;
}

a:hover {
transition-duration: 2s;
}
7 changes: 2 additions & 5 deletions development/styles/app.scss
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
$main : black ;
$night : white ;
$bg : gray ;
$bg : white ;
$bgNight : black ;
@import "app.css";

body {
color : $main ;
background-color : $bg ;
}

.welcome {
font-style: italic ;
}
49 changes: 42 additions & 7 deletions dist/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@
if( ! function_exists( 'shell_exec' ) )
die( 'shell_exec required' );

function ytdl_find_extension( $fileName ){
if( stristr( $fileName , '.' ) === false )
return "mp4" ;
$fileName = explode( '.' , $fileName );
if( ! is_array( $fileName ) || count( $fileName ) <= 1 )
return "mp4" ;
return $fileName[ count( $fileName ) - 1 ]; // extension
}

function ytdl_make_name( $fileName ){
$ext = ytdl_find_extension( $fileName );
// remove extension name
$fileName = str_ireplace( '.' . $ext , '' , $fileName ) ;
// clean name
$fileName = str_ireplace( ' ' , '_' , $fileName );
return preg_replace("/[^A-Za-z0-9_.-]/", '', $fileName ) . '.' . $ext ;
}

// grab them
$isLinux = PHP_OS === "Linux" ;
$ytdl_win = "http://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe";
Expand All @@ -33,28 +51,45 @@
if( ! isset( $_POST[ 'link' ] ) )
die( 'No link' );
$link = $_POST[ 'link' ];
$quality = isset( $_POST['quality'] ) ? $_POST['quality'] : null;
if( strtolower( $link ) === 'clear' || strtolower( $link ) === 'clean' ){
shell_exec( $isLinux ? 'rm *.mp4' : 'del *.mp4' );
shell_exec( $isLinux ? 'rm *.webm' : 'del *.webm' );
die( 'all videos deleted' ) ;
} else if( stristr( $link , 'cleanfile:' ) !== false ){
$link = str_ireplace( 'cleanfile:' , '' , $link );
$link = shell_exec( $isLinux ? 'rm ' . $link : 'del "' . $link . '"' );
die( 'video deleted');
} else if( strtolower( $link ) === 'list' || strtolower( $link ) === 'videos' ){
} else if( stristr( $link , 'renamefile:' ) !== false ){
// old name
$link = str_ireplace( 'renamefile:' , '' , $link );
// new name
$nName = ytdl_make_name( isset( $_POST[ 'to' ] ) ? $_POST[ 'to' ] : $link );
if( rename( $link , $nName ) ) // rename!
die( 'video renamed:' . $nName );
die( 'video renamed:' . $link ); // fail
} else if( strtolower( $link ) === 'list' || strtolower( $link ) === 'videos' ){
$scDir = scandir( '.' );
$videos = 'videos-list:{';
foreach( $scDir as $file )
if( stristr( $file , '.mp4' ) )
if( stristr( $file , '.mp4' ) || stristr( $file , '.webm' ) )
$videos .= "\n\"" . $file . '" : "' . $file . '" ,' ;
$videos = trim( $videos , ' ,' );
$videos .= '}' ;
die( $videos );
} // else :
$down = shell_exec( $exFile . ' ' . $link );
$down = $down = $quality ? ' -S "res:'. $quality . '"' : '' ;
$down = shell_exec( $exFile . $down . ' ' . $link );
$down = explode( "[download]" , $down );
if( is_array( $down ) ) foreach( $down as $intel ){
$intel = trim( $intel , " /\\\r\n\t" );
if( stristr( $intel , 'Destination: ' ) !== false )
die( str_ireplace( 'Destination: ' , '' , $intel ) );
} $down = 'Download failed' ;
die( $down ); ?>
if( stristr( $intel , 'Destination:' ) !== false ){
// get file name
$intel = trim( str_ireplace( 'Destination:' , '' , $intel ) );
// make new clean name
$link = ytdl_make_name( $intel ) ;
if( rename( $intel , $link ) ) // rename!
die( $link );
die( $intel );
}
} die( 'Download failed : change quality options' ); ?>
2 changes: 1 addition & 1 deletion dist/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
?><!doctype html>
<html class="col">
<head>
<title>Welcome</title>
<title>Welcome To YTDL</title>
<meta name='charset' content='text/html; charset=UTF-8' />
<meta name='viewport' content='width=device-width, initial-scale=1.0, shrink-to-fit=no, user-scalable=yes' />
<link href="<?php print $wrw; ?>styles/bootstrap.min.css" rel='stylesheet' type='text/css' />
Expand Down
Loading

0 comments on commit ba81425

Please sign in to comment.