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 @@
- Convert
+
+
+
+
+ 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.
+
+
+
+
+ Convert
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