diff --git a/composer.json b/composer.json index f5f05ad..a963433 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ "phpstan/phpstan": "^2.0" }, "config": { - "sort-packages": true + "sort-packages": true, + "process-timeout": 600 } } diff --git a/public/convert.php b/public/convert.php index 7fcb2a5..faefd95 100644 --- a/public/convert.php +++ b/public/convert.php @@ -11,6 +11,8 @@ if(isset($_GET["youtubelink"]) && !empty($_GET["youtubelink"])) { $youtubelink = $_GET["youtubelink"]; + $startAt = $_GET["startAt"] ?? null; + $endAt = $_GET["endAt"] ?? null; $format = $_GET['format'] ?? 'mp3'; if(!in_array($format, POSSIBLE_FORMATS)) @@ -31,6 +33,36 @@ $exists = file_exists(env('DOWNLOAD_FOLDER').$id.".".$format); + if (!empty($startAt) || !empty($endAt)) + { + if (!empty($startAt) && !empty($endAt)) + { + if ((int)$startAt >= (int)$endAt) + { + http_response_code(400); + die(json_encode(array("error" => true, "message" => "Invalid time range: startAt must be less than endAt"))); + } + } + + $query = parse_url($youtubelink, PHP_URL_QUERY); + parse_str($query, $params); + + if (!empty($startAt)) + $params['start'] = $startAt; + + if (!empty($endAt)) + $params['end'] = $endAt; + + $youtubelink = strtok($youtubelink, '?') . '?' . http_build_query($params); + + if ($exists) + { + //todo this can probably go when youtube-dl-php supports --force-overwrites options + unlink(env('DOWNLOAD_FOLDER').$id.".".$format); + $exists = false; + } + } + if(env('DOWNLOAD_MAX_LENGTH', 0) > 0 || $exists) { try { @@ -69,6 +101,9 @@ ->cookies(file_exists(env('COOKIE_FILE')) ? env('COOKIE_FILE') : null) ->url($youtubelink); + if (!empty($startAt) || !empty($endAt)) + $options = $options->downloadSections('*from-url'); + if($format == 'mp3') { $options = $options->extractAudio(true) diff --git a/public/index.php b/public/index.php index 00bc037..bc202a0 100644 --- a/public/index.php +++ b/public/index.php @@ -23,7 +23,7 @@
- +
@@ -39,7 +39,35 @@
- +
+
+
+ + + + +
+ + +
+ +
+ + +
+ + + + +
+
+ Specify start and/or end times (in seconds) to trim the video. Leave both empty to download the complete video.
+ Start time only: downloads from that point to the end. End time only: downloads from the beginning to that point. +
+
+
+ + diff --git a/public/script.js b/public/script.js index a530e20..eb88ae0 100644 --- a/public/script.js +++ b/public/script.js @@ -9,11 +9,19 @@ document.addEventListener('DOMContentLoaded', () => { const submitButton = frmConvert.querySelector("button[type=submit]"); const link = document.getElementById('link').value; const format = document.getElementById('format').value; + const startAt = document.getElementById('startAt').value; + const endAt = document.getElementById('endAt').value; submitButton.classList.add("disabled"); submitButton.innerHTML = " Converting..."; - const endpoint = `${frmConvert.getAttribute("action")}?youtubelink=${link}&format=${format}`; + let endpoint = `${frmConvert.getAttribute("action")}?youtubelink=${link}&format=${format}`; + + if (startAt.length > 0) + endpoint += `&startAt=${startAt}`; + + if (endAt.length > 0) + endpoint += `&endAt=${endAt}`; fetch(endpoint) .then(response => response.json()) @@ -168,4 +176,37 @@ function handleInfoResponse(data, submitButton) tableCells[9].innerText = data.title; tableCells[10].innerHTML = `${data.url}`; } +} + +function fillStartEnd() +{ + const elLink = document.getElementById('link'); + const elStart = document.getElementById('startAt'); + const elEnd = document.getElementById('endAt'); + + const url = new URL(elLink.value); + const start = url.searchParams.get("start") || url.searchParams.get("t"); + const end = url.searchParams.get("end"); + + if (start && start.length > 0) + { + elStart.value = start; + elStart.setAttribute('readonly', 'readonly'); + } + else + { + elStart.value = ''; + elStart.removeAttribute('readonly'); + } + + if (end && end.length > 0) + { + elEnd.value = end; + elEnd.setAttribute('readonly', 'readonly'); + } + else + { + elEnd.value = ''; + elEnd.removeAttribute('readonly'); + } } \ No newline at end of file