".$this->config["bin"]."
, see yt-dlp site !";
179 | }
180 |
181 | $this->check_outuput_folder();
182 |
183 | if($audio_only)
184 | {
185 | if($this->is_extracter_installed() != 0)
186 | {
187 | $this->errors[] = "Install an audio extracter (ex: ffmpeg) !";
188 | }
189 | }
190 |
191 | if(isset($this->errors) && count($this->errors) > 0)
192 | {
193 | $_SESSION['errors'] = $this->errors;
194 | return false;
195 | }
196 |
197 | return true;
198 | }
199 |
200 | private function is_youtubedl_installed()
201 | {
202 | exec("which ".$this->config["bin"], $out, $r);
203 | return $r;
204 | }
205 |
206 | public static function get_youtubedl_version()
207 | {
208 | $config = require dirname(__DIR__).'/config/config.php';
209 | $soutput = shell_exec($config["bin"]." --version");
210 | return trim($soutput);
211 | }
212 |
213 | private function is_extracter_installed()
214 | {
215 | exec("which ".$this->config["extracter"], $out, $r);
216 | return $r;
217 | }
218 |
219 | private function is_python_installed()
220 | {
221 | exec("which python3", $out, $r);
222 | return $r;
223 | }
224 |
225 | private function is_valid_url($url)
226 | {
227 | return filter_var($url, FILTER_VALIDATE_URL);
228 | }
229 |
230 | private function check_outuput_folder()
231 | {
232 | if(!is_dir($this->download_path))
233 | {
234 | //Folder doesn't exist
235 | if(!mkdir($this->download_path, 0775))
236 | {
237 | $this->errors[] = "Output folder doesn't exist and creation failed! (".$this->download_path.")";
238 | }
239 | }
240 | else
241 | {
242 | //Exists but can I write ?
243 | if(!is_writable($this->download_path))
244 | {
245 | $this->errors[] = "Output folder isn't writable! (".$this->download_path.")";
246 | }
247 | }
248 |
249 | // LOG folder
250 | if($this->config["log"])
251 | {
252 | if(!is_dir($this->log_path))
253 | {
254 | //Folder doesn't exist
255 | if(!mkdir($this->log_path, 0775))
256 | {
257 | $this->errors[] = "Log folder doesn't exist and creation failed! (".$this->log_path.")";
258 | }
259 | }
260 | else
261 | {
262 | //Exists but can I write ?
263 | if(!is_writable($this->log_path))
264 | {
265 | $this->errors[] = "Log folder isn't writable! (".$this->log_path.")";
266 | }
267 | }
268 | }
269 |
270 | }
271 |
272 | private function do_download($audio_only)
273 | {
274 | $cmd = $this->config["bin"];
275 | $cmd .= " --ignore-error -o ".$this->download_path."/";
276 | $cmd .= escapeshellarg($this->outfilename);
277 |
278 | if ($this->vformat)
279 | {
280 | $cmd .= " --format ";
281 | $cmd .= escapeshellarg($this->vformat);
282 | }
283 | if($audio_only)
284 | {
285 | $cmd .= " -x ";
286 | }
287 | $cmd .= " --restrict-filenames"; // --restrict-filenames is for specials chars
288 | foreach($this->urls as $url)
289 | {
290 | $cmd .= " ".escapeshellarg($url);
291 | }
292 | if($this->config["log"])
293 | {
294 | $cmd = "{ echo Command: ".escapeshellarg($cmd)."; ".$cmd." ; }";
295 | $cmd .= " > ".$this->log_path."/$(date +\"%Y-%m-%d_%H-%M-%S-%N\").txt";
296 | }
297 | else
298 | {
299 | $cmd .= " > /dev/null ";
300 | }
301 |
302 | $cmd .= " & echo $!";
303 |
304 | shell_exec($cmd);
305 | }
306 |
307 | private function do_info()
308 | {
309 | $cmd = $this->config["bin"]." -J ";
310 |
311 | foreach($this->urls as $url)
312 | {
313 | $cmd .= " ".escapeshellarg($url);
314 | }
315 |
316 | if ($this->is_python_installed() == 0)
317 | {
318 | $cmd .= " | python3 -m json.tool";
319 | }
320 |
321 | $soutput = shell_exec($cmd);
322 | if (!$soutput)
323 | {
324 | $this->errors[] = "No video found";
325 | }
326 | return $soutput;
327 |
328 | }
329 | }
330 |
331 | ?>
332 |
--------------------------------------------------------------------------------
/class/FileHandler.php:
--------------------------------------------------------------------------------
1 | config = require dirname(__DIR__).'/config/config.php';
11 | }
12 |
13 | public function listFiles()
14 | {
15 | $files = [];
16 |
17 | if(!$this->outuput_folder_exists())
18 | return;
19 |
20 | $folder = $this->get_downloads_folder().'/';
21 |
22 | foreach(glob($folder.'*.*', GLOB_BRACE) as $file)
23 | {
24 | $content = [];
25 | $content["name"] = str_replace($folder, "", $file);
26 | $content["size"] = $this->to_human_filesize(filesize($file));
27 |
28 | if (preg_match($this->re_partial, $content["name"]) === 0) {
29 | $files[] = $content;
30 | }
31 |
32 | }
33 |
34 | return $files;
35 | }
36 |
37 | public function listParts()
38 | {
39 | $files = [];
40 |
41 | if(!$this->outuput_folder_exists())
42 | return;
43 |
44 | $folder = $this->get_downloads_folder().'/';
45 |
46 | foreach(glob($folder.'*.*', GLOB_BRACE) as $file)
47 | {
48 | $content = [];
49 | $content["name"] = str_replace($folder, "", $file);
50 | $content["size"] = $this->to_human_filesize(filesize($file));
51 |
52 | if (preg_match($this->re_partial, $content["name"]) !== 0) {
53 | $files[] = $content;
54 | }
55 |
56 | }
57 |
58 | return $files;
59 | }
60 |
61 | public function is_log_enabled()
62 | {
63 | return !!($this->config["log"]);
64 | }
65 |
66 | public function countLogs()
67 | {
68 | if(!$this->config["log"])
69 | return;
70 |
71 | if(!$this->logs_folder_exists())
72 | return;
73 |
74 | $folder = $this->get_logs_folder().'/';
75 | return count(glob($folder.'*.txt', GLOB_BRACE));
76 | }
77 |
78 | public function listLogs()
79 | {
80 | $files = [];
81 |
82 | if(!$this->config["log"])
83 | return;
84 |
85 | if(!$this->logs_folder_exists())
86 | return;
87 |
88 | $folder = $this->get_logs_folder().'/';
89 |
90 | foreach(glob($folder.'*.txt', GLOB_BRACE) as $file)
91 | {
92 | $content = [];
93 | $content["name"] = str_replace($folder, "", $file);
94 | $content["size"] = $this->to_human_filesize(filesize($file));
95 |
96 | try {
97 | $lines = explode("\r", file_get_contents($file));
98 | $content["lastline"] = array_slice($lines, -1)[0];
99 | $content["100"] = strpos($lines[count($lines)-1], ' 100% of ') > 0;
100 | } catch (Exception $e) {
101 | $content["lastline"] = '';
102 | $content["100"] = False;
103 | }
104 | try {
105 | $handle = fopen($file, 'r');
106 | fseek($handle, filesize($file) - 1);
107 | $lastc = fgets($handle);
108 | fclose($handle);
109 | $content["ended"] = ($lastc === "\n");
110 | } catch (Exception $e) {
111 | $content["ended"] = False;
112 | }
113 |
114 |
115 | $files[] = $content;
116 | }
117 |
118 | return $files;
119 | }
120 |
121 | public function delete($id)
122 | {
123 | $folder = $this->get_downloads_folder().'/';
124 |
125 | foreach(glob($folder.'*.*', GLOB_BRACE) as $file)
126 | {
127 | if(sha1(str_replace($folder, "", $file)) == $id)
128 | {
129 | unlink($file);
130 | }
131 | }
132 | }
133 |
134 | public function deleteLog($id)
135 | {
136 | $folder = $this->get_logs_folder().'/';
137 |
138 | foreach(glob($folder.'*.txt', GLOB_BRACE) as $file)
139 | {
140 | if(sha1(str_replace($folder, "", $file)) == $id)
141 | {
142 | unlink($file);
143 | }
144 | }
145 | }
146 |
147 | private function outuput_folder_exists()
148 | {
149 | if(!is_dir($this->get_downloads_folder()))
150 | {
151 | //Folder doesn't exist
152 | if(!mkdir($this->get_downloads_folder(),0777))
153 | {
154 | return false; //No folder and creation failed
155 | }
156 | }
157 |
158 | return true;
159 | }
160 |
161 | public function to_human_filesize($bytes, $decimals = 1)
162 | {
163 | $sz = 'BKMGTP';
164 | $factor = floor((strlen($bytes) - 1) / 3);
165 | return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
166 | }
167 |
168 | public function free_space()
169 | {
170 | return $this->to_human_filesize(disk_free_space(realpath($this->get_downloads_folder())));
171 | }
172 |
173 | public function used_space()
174 | {
175 | $path = realpath($this->get_downloads_folder());
176 | $bytestotal = 0;
177 | foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)) as $object){
178 | $bytestotal += $object->getSize();
179 | }
180 | return $this->to_human_filesize($bytestotal);
181 | }
182 |
183 | public function get_downloads_folder()
184 | {
185 | $path = $this->config["outputFolder"];
186 | if(strpos($path , "/") !== 0)
187 | {
188 | $path = dirname(__DIR__).'/' . $path;
189 | }
190 | return $path;
191 | }
192 |
193 | public function get_logs_folder()
194 | {
195 | $path = $this->config["logFolder"];
196 | if(strpos($path , "/") !== 0)
197 | {
198 | $path = dirname(__DIR__).'/' . $path;
199 | }
200 | return $path;
201 | }
202 |
203 | public function get_relative_downloads_folder()
204 | {
205 | $path = $this->config["outputFolder"];
206 | if(strpos($path , "/") !== 0)
207 | {
208 | return $this->config["outputFolder"];
209 | }
210 | return false;
211 | }
212 |
213 | public function get_relative_log_folder()
214 | {
215 | $path = $this->config["logFolder"];
216 | if(strpos($path , "/") !== 0)
217 | {
218 | return $this->config["logFolder"];;
219 | }
220 | return false;
221 | }
222 |
223 | private function logs_folder_exists()
224 | {
225 | if(!is_dir($this->get_logs_folder()))
226 | {
227 | //Folder doesn't exist
228 | if(!mkdir($this->get_logs_folder(),0777))
229 | {
230 | return false; //No folder and creation failed
231 | }
232 | }
233 |
234 | return true;
235 | }
236 | }
237 |
238 | ?>
239 |
--------------------------------------------------------------------------------
/class/Session.php:
--------------------------------------------------------------------------------
1 | config = require dirname(__DIR__).'/config/config.php';
16 | $session_expire = min(2147483647 - time() - 1, max($this->config["session_lifetime"], 86400));
17 | $session_name = "ydlw_sid";
18 |
19 | if ((!empty($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] != 'off')) || $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
20 | ini_set("session.cookie_secure", true);
21 | }
22 |
23 | ini_set("session.gc_probability", 75);
24 | ini_set("session.name", $session_name);
25 | ini_set("session.use_only_cookies", true);
26 | ini_set("session.gc_maxlifetime", $session_expire);
27 | ini_set("session.cookie_lifetime", min(0, $this->config["session_lifetime"]));
28 | session_start();
29 |
30 | if($this->config["security"])
31 | {
32 | if(!isset($_SESSION["logged_in"]))
33 | {
34 | $_SESSION["logged_in"] = false;
35 | }
36 | }
37 | else
38 | {
39 | $_SESSION["logged_in"] = true;
40 | }
41 | }
42 |
43 | public static function getInstance()
44 | {
45 | if(is_null(self::$_instance))
46 | {
47 | self::$_instance = new Session();
48 | }
49 |
50 | return self::$_instance;
51 | }
52 |
53 | public function login($password)
54 | {
55 | if($this->config["password"] === md5($password))
56 | {
57 | $_SESSION["logged_in"] = true;
58 | return true;
59 | }
60 | else
61 | {
62 | $_SESSION["logged_in"] = false;
63 | return false;
64 | }
65 | }
66 |
67 | public function is_logged_in()
68 | {
69 | return $_SESSION["logged_in"];
70 | }
71 |
72 | public function logout()
73 | {
74 | $_SESSION = array();
75 | session_destroy();
76 | }
77 | }
78 |
79 | ?>
80 |
--------------------------------------------------------------------------------
/config/config.php.TEMPLATE:
--------------------------------------------------------------------------------
1 | "/usr/local/bin/yt-dlp",
8 | "security" => true,
9 | "password" => "63a9f0ea7bb98050796b649e85481845",
10 | "outputFolder" => "downloads",
11 | "extracter" => "ffmpeg",
12 | "log" => true,
13 | "outfilename" => "%(title).200s-%(id)s.%(ext)s",
14 | "logFolder" => "logs",
15 | "max_dl" => 3,
16 | "session_lifetime" => 86400);
17 |
18 | ?>
19 |
--------------------------------------------------------------------------------
/css/custom.css:
--------------------------------------------------------------------------------
1 | /* Custom CSS */
2 | .footer {
3 | position: fixed;
4 | bottom: 20px;
5 | width: 100%;
6 | height: 40px;
7 | background-color: #f5f5f5;
8 | }
--------------------------------------------------------------------------------
/css/input.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | .nav-menu>li>a {
7 | @apply flex gap-1 items-baseline;
8 | }
9 |
10 | .nav-menu>li>a>spana {
11 | @apply inline-block;
12 | @apply align-middle;
13 | }
14 |
15 | .nav-menu>li>a>span svg {
16 | @apply hidden;
17 | }
18 |
19 | @media screen(md) {
20 | .nav-menu>li>a>span svg {
21 | @apply inline-block;
22 | }
23 | }
24 |
25 | .infotext p {
26 | @apply py-1;
27 |
28 | }
29 |
30 | .filelist tbody td {
31 | @apply p-1;
32 | }
33 | @media screen(lg) {
34 | .filelist tbody td {
35 | @apply p-2;
36 | }
37 | }
38 | .filelist .btn-delete {
39 | @apply text-white bg-red-600 hover:bg-red-800 focus:ring-2 focus:ring-red-200 font-medium rounded-lg text-sm px-2 py-0.5 focus:ring-red-800;
40 | }
41 | @media screen(lg) {
42 | .filelist .btn-delete {
43 | @apply px-3 py-1.5;
44 | }
45 | }
46 | .dropdown:focus-within .dropdown-menu {
47 | display:block;
48 | }
49 | }
--------------------------------------------------------------------------------
/css/output.css:
--------------------------------------------------------------------------------
1 | *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.15 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]:where(:not([hidden=until-found])){display:none}.nav-menu>li>a{align-items:baseline;display:flex;gap:.25rem}.nav-menu>li>a>spana{display:inline-block;vertical-align:middle}.nav-menu>li>a>span svg{display:none}@media (min-width:768px){.nav-menu>li>a>span svg{display:inline-block}}.infotext p{padding-bottom:.25rem;padding-top:.25rem}.filelist tbody td{padding:.25rem}@media (min-width:1024px){.filelist tbody td{padding:.5rem}}.filelist .btn-delete{border-radius:.5rem;--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1));font-size:.875rem;font-weight:500;line-height:1.25rem;padding:.125rem .5rem;--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.filelist .btn-delete:hover{--tw-bg-opacity:1;background-color:rgb(153 27 27/var(--tw-bg-opacity,1))}.filelist .btn-delete:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:rgb(254 202 202/var(--tw-ring-opacity,1));--tw-ring-opacity:1;--tw-ring-color:rgb(153 27 27/var(--tw-ring-opacity,1))}@media (min-width:1024px){.filelist .btn-delete{padding:.375rem .75rem}}.dropdown:focus-within .dropdown-menu{display:block}.absolute{position:absolute}.relative{position:relative}.right-0{right:0}.mx-4{margin-left:1rem;margin-right:1rem}.my-1{margin-bottom:.25rem;margin-top:.25rem}.my-2{margin-bottom:.5rem;margin-top:.5rem}.my-3{margin-bottom:.75rem;margin-top:.75rem}.my-4{margin-bottom:1rem;margin-top:1rem}.mt-2{margin-top:.5rem}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.hidden{display:none}.w-56{width:14rem}.w-full{width:100%}.min-w-full{min-width:100%}.max-w-md{max-width:28rem}.flex-auto{flex:1 1 auto}.table-auto{table-layout:auto}.border-collapse{border-collapse:collapse}.origin-top-right{transform-origin:top right}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-bottom-width:calc(1px*var(--tw-divide-y-reverse));border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity,1))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity,1))}.overflow-hidden{overflow:hidden}.text-ellipsis{text-overflow:ellipsis}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-l-sm{border-bottom-left-radius:.125rem}.rounded-l-sm,.rounded-t-sm{border-top-left-radius:.125rem}.rounded-t-sm{border-top-right-radius:.125rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l-4{border-left-width:4px}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.border-yellow-500{--tw-border-opacity:1;border-color:rgb(234 179 8/var(--tw-border-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity,1))}.p-1{padding:.25rem}.p-4{padding:1rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-bottom:.125rem;padding-top:.125rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.text-left{text-align:left}.text-2xl{font-size:1.5rem;line-height:2rem}.text-sm{font-size:.875rem;line-height:1.25rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-5{line-height:1.25rem}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-yellow-700{--tw-text-opacity:1;color:rgb(161 98 7/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.no-underline{text-decoration-line:none}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.odd\:bg-gray-100:nth-child(odd){--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.visited\:text-purple-600:visited{color:#9333ea}.hover\:bg-blue-800:hover{--tw-bg-opacity:1;background-color:rgb(30 64 175/var(--tw-bg-opacity,1))}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.hover\:text-blue-800:hover{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:decoration-transparent:hover{text-decoration-color:transparent}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-blue-200:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(191 219 254/var(--tw-ring-opacity,1))}.focus\:ring-blue-800:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(30 64 175/var(--tw-ring-opacity,1))}.active\:text-blue-900:active{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity,1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-400:disabled{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.disabled\:from-gray-400:disabled{--tw-gradient-from:#9ca3af var(--tw-gradient-from-position);--tw-gradient-to:rgba(156,163,175,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.disabled\:shadow-gray-800:disabled{--tw-shadow-color:#1f2937;--tw-shadow:var(--tw-shadow-colored)}@media (min-width:768px){.md\:mx-8{margin-left:2rem;margin-right:2rem}.md\:inline-block{display:inline-block}.md\:gap-2{gap:.5rem}.md\:gap-3{gap:.75rem}}@media (min-width:1024px){.lg\:mx-16{margin-left:4rem;margin-right:4rem}.lg\:my-8{margin-bottom:2rem;margin-top:2rem}.lg\:line-clamp-1{display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:1}.lg\:max-w-screen-md{max-width:768px}.lg\:gap-3{gap:.75rem}.lg\:gap-5{gap:1.25rem}.lg\:p-2{padding:.5rem}.lg\:px-3{padding-left:.75rem;padding-right:.75rem}.lg\:px-4{padding-left:1rem;padding-right:1rem}.lg\:py-1{padding-bottom:.25rem;padding-top:.25rem}.lg\:py-1\.5{padding-bottom:.375rem;padding-top:.375rem}.lg\:py-2{padding-bottom:.5rem;padding-top:.5rem}.lg\:py-3{padding-bottom:.75rem;padding-top:.75rem}.lg\:text-4xl{font-size:2.25rem;line-height:2.5rem}.lg\:text-xl{font-size:1.25rem;line-height:1.75rem}.lg\:font-semibold{font-weight:600}}@media (min-width:1280px){.xl\:mx-32{margin-left:8rem;margin-right:8rem}.xl\:max-w-screen-lg{max-width:1024px}.xl\:gap-5{gap:1.25rem}.xl\:text-xl{font-size:1.25rem;line-height:1.75rem}}
--------------------------------------------------------------------------------
/downloads/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timendum/Youtube-dl-WebUI/012490eca7ec5e2c75e301dbb6dbd8d784f837fb/downloads/.keep
--------------------------------------------------------------------------------
/favicon_144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timendum/Youtube-dl-WebUI/012490eca7ec5e2c75e301dbb6dbd8d784f837fb/favicon_144.png
--------------------------------------------------------------------------------
/img/list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timendum/Youtube-dl-WebUI/012490eca7ec5e2c75e301dbb6dbd8d784f837fb/img/list.png
--------------------------------------------------------------------------------
/img/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/timendum/Youtube-dl-WebUI/012490eca7ec5e2c75e301dbb6dbd8d784f837fb/img/main.png
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | is_logged_in())
10 | {
11 | header("Location: login.php");
12 | exit;
13 | }
14 | else
15 | {
16 | if(isset($_GET['kill']) && !empty($_GET['kill']) && $_GET['kill'] === "all")
17 | {
18 | Downloader::kill_them_all();
19 | }
20 |
21 | if(isset($_POST['urls']) && !empty($_POST['urls']))
22 | {
23 | $audio_only = false;
24 | if(isset($_POST['audio']) && !empty($_POST['audio']))
25 | {
26 | $audio_only = true;
27 | }
28 |
29 | $outfilename = False;
30 | if(isset($_POST['outfilename']) && !empty($_POST['outfilename']))
31 | {
32 | $outfilename = $_POST['outfilename'];
33 | }
34 |
35 | $vformat = False;
36 | if(isset($_POST['vformat']) && !empty($_POST['vformat']))
37 | {
38 | $vformat = $_POST['vformat'];
39 | }
40 |
41 | $downloader = new Downloader($_POST['urls']);
42 | $downloader->download($audio_only, $outfilename, $vformat);
43 |
44 | if(!isset($_SESSION['errors']))
45 | {
46 | header("Location: index.php");
47 | exit;
48 | }
49 | }
50 | }
51 |
52 | require 'views/header.php';
53 | ?>
54 | Free space: free_space(); ?>
97 |Used space: used_space(); ?>
98 |Download folder: get_downloads_folder(); ?>
Youtube-dl version:
100 |How does it work ?
106 |Simply paste your video link in the field and click "Download"
107 |With which sites does it work?
108 |Here's a list of the supported sites
109 |How can I download the video on my computer?
110 |Go to List of files -> choose one -> right click on the link -> "Save target as ..."
111 |What's Filename or Format field?
112 |They are optional, see the official documentation about Format selection or Output template
113 |Filename | 37 |Size | 38 |Delete link | 39 |get_relative_downloads_folder()).'/'.rawurlencode($f["name"])."\" download title=\"".htmlspecialchars($f["name"])."\">".htmlspecialchars($f["name"])." | "; 49 | } 50 | else 51 | { 52 | echo "".htmlspecialchars($f["name"])." | "; 53 | } 54 | echo "".$f["size"]." | "; 55 | echo "Delete | "; 56 | echo "\n"; 57 | } 58 | ?> 59 | 60 |
---|
Filename | 77 |Size | 78 |Delete link | 79 |get_relative_downloads_folder()).'/'.rawurlencode($f["name"])."\" download title=\"".htmlspecialchars($f["name"])."\">".htmlspecialchars($f["name"])." | "; 89 | } 90 | else 91 | { 92 | echo "".htmlspecialchars($f["name"])." | "; 93 | } 94 | echo "".$f["size"]." | "; 95 | echo "Delete | "; 96 | echo "\n"; 97 | } 98 | ?> 99 | 100 |
---|
Timestamp | 35 |Ended? | 36 |Ok? | 37 |Size | 38 |Delete link | 39 |get_relative_log_folder()).'/'.rawurlencode($f["name"])."\" download title=\"".htmlspecialchars($f["name"])."\">".htmlspecialchars($f["name"])." | "; 50 | } 51 | else 52 | { 53 | echo "".$f["name"]." ".$f["lastline"]." | ";
54 | }
55 | echo "".($f["ended"] ? '✓' : '')." | "; 56 | echo "".($f["100"] ? '✓' : '')." | "; 57 | echo "".$f["size"]." | "; 58 | echo "Delete | "; 59 | echo "\n"; 60 | } 61 | ?> 62 | 63 |
---|