diff --git a/TODO.md b/TODO.md
index f8f759c..3ab5925 100644
--- a/TODO.md
+++ b/TODO.md
@@ -17,7 +17,8 @@
- [x] Add CURLOPT_HEADERFUNCTION to capture response headers
- [x] Disable CURLOPT_FOLLOWLOCATION to preserve Location header
- [x] Add cancelUpload() helper for Delete-on-error cleanup
-- [ ] Test with actual PeerTube instance
+- [x] PeerTube upload fixed — simple multipart POST /api/v1/videos/upload works
+- [x] Add upload-progress.js — XHR form submit with progress bar for admin add/edit forms
## HTMX Toast Feedback for Settings Checkboxes (contenus.php)
diff --git a/app/public/admin/add.php b/app/public/admin/add.php
index 81f5014..59f86d0 100644
--- a/app/public/admin/add.php
+++ b/app/public/admin/add.php
@@ -55,7 +55,7 @@ function wasSelected($key, $value) {
$isAdmin = true;
$bodyClass = 'admin-body';
$extraCss = ['/assets/css/form.css', '/assets/css/filepond.min.css', '/assets/css/filepond-plugin-image-preview.min.css'];
-$extraJs = ['/assets/js/filepond.min.js', '/assets/js/filepond-plugin-file-validate-type.min.js', '/assets/js/filepond-plugin-file-validate-size.min.js', '/assets/js/filepond-plugin-image-preview.min.js', '/assets/js/filepond-plugin-image-exif-orientation.min.js', '/assets/js/file-upload-filepond.js', '/assets/js/beforeunload-guard.js'];
+$extraJs = ['/assets/js/filepond.min.js', '/assets/js/filepond-plugin-file-validate-type.min.js', '/assets/js/filepond-plugin-file-validate-size.min.js', '/assets/js/filepond-plugin-image-preview.min.js', '/assets/js/filepond-plugin-image-exif-orientation.min.js', '/assets/js/file-upload-filepond.js', '/assets/js/beforeunload-guard.js', '/assets/js/upload-progress.js'];
require_once APP_ROOT . '/templates/head.php';
include APP_ROOT . '/templates/header.php';
include APP_ROOT . '/templates/admin/add.php';
diff --git a/app/public/admin/edit.php b/app/public/admin/edit.php
index 3a2d4a8..52d9313 100644
--- a/app/public/admin/edit.php
+++ b/app/public/admin/edit.php
@@ -40,7 +40,7 @@ try {
$isAdmin = true; $bodyClass = 'admin-body';
$extraCss = ['/assets/css/form.css', '/assets/css/filepond.min.css', '/assets/css/filepond-plugin-image-preview.min.css'];
-$extraJs = ['/assets/js/filepond.min.js', '/assets/js/filepond-plugin-file-validate-type.min.js', '/assets/js/filepond-plugin-file-validate-size.min.js', '/assets/js/filepond-plugin-image-preview.min.js', '/assets/js/filepond-plugin-image-exif-orientation.min.js', '/assets/js/file-upload-filepond.js', '/assets/js/beforeunload-guard.js'];
+$extraJs = ['/assets/js/filepond.min.js', '/assets/js/filepond-plugin-file-validate-type.min.js', '/assets/js/filepond-plugin-file-validate-size.min.js', '/assets/js/filepond-plugin-image-preview.min.js', '/assets/js/filepond-plugin-image-exif-orientation.min.js', '/assets/js/file-upload-filepond.js', '/assets/js/beforeunload-guard.js', '/assets/js/upload-progress.js'];
require_once APP_ROOT . '/templates/head.php';
include APP_ROOT . '/templates/header.php';
include APP_ROOT . '/templates/admin/edit.php';
diff --git a/app/public/assets/js/upload-progress.js b/app/public/assets/js/upload-progress.js
new file mode 100644
index 0000000..964f60f
--- /dev/null
+++ b/app/public/assets/js/upload-progress.js
@@ -0,0 +1,96 @@
+/**
+ * upload-progress.js
+ *
+ * Intercepts admin form submissions (add.php / edit.php) and submits via
+ * XMLHttpRequest to display a progress bar for large file uploads.
+ * Falls back to native form POST when JavaScript is unavailable.
+ *
+ * Requires an element with id="upload-progress-bar" inside the form.
+ * The progress bar is normally hidden (display:none), shown only during upload.
+ */
+(() => {
+ 'use strict';
+
+ const FORMS = document.querySelectorAll('form[data-upload-progress]');
+ if (!FORMS.length) return;
+
+ for (const form of FORMS) {
+ const progressWrap = form.querySelector('#upload-progress-wrap');
+ const progressBar = form.querySelector('#upload-progress-bar');
+ const progressText = form.querySelector('#upload-progress-text');
+ const submitBtn = form.querySelector('button[type="submit"]');
+
+ if (!progressBar || !progressWrap) continue;
+
+ form.addEventListener('submit', function (e) {
+ // Only intercept if files are actually attached (FilePond inputs have files)
+ const fileInputs = form.querySelectorAll('input[type="file"]');
+ let hasFiles = false;
+ for (const fi of fileInputs) {
+ if (fi.files && fi.files.length > 0) {
+ hasFiles = true;
+ break;
+ }
+ }
+ if (!hasFiles) return; // let native submit handle it
+
+ e.preventDefault();
+
+ // Show progress bar
+ progressWrap.style.display = 'block';
+ progressBar.value = 0;
+ progressText.textContent = '0%';
+ if (submitBtn) submitBtn.disabled = true;
+
+ // Build FormData
+ const fd = new FormData(form);
+ // Ensure any FilePond-managed files are included — FilePond with
+ // storeAsFile:true copies files into the .files, so FormData
+ // picks them up automatically from the DOM inputs.
+ // But we must also respect queue_order hidden inputs.
+ // FormData(form) already handles this since it reads all form fields.
+
+ const xhr = new XMLHttpRequest();
+
+ xhr.upload.addEventListener('progress', function (evt) {
+ if (evt.lengthComputable) {
+ const pct = Math.round((evt.loaded / evt.total) * 100);
+ progressBar.value = pct;
+ progressText.textContent = pct + '%';
+ }
+ });
+
+ xhr.addEventListener('load', function () {
+ // Server returns a redirect (302) on success, or re-renders the form on error.
+ // We can't follow 302 with XHR directly — the response body is the target page.
+ // Check if we got a redirect by examining the response URL.
+ const finalUrl = xhr.responseURL || '';
+ const isRedirect = xhr.status >= 200 && xhr.status < 300 && finalUrl !== '' && !finalUrl.endsWith(form.action);
+
+ if (isRedirect) {
+ // Success — navigate to the redirect target
+ window.location.href = finalUrl;
+ } else {
+ // Error — the server returned the form HTML with flash messages.
+ // Replace the current page content.
+ document.open();
+ document.write(xhr.responseText);
+ document.close();
+ }
+ });
+
+ xhr.addEventListener('error', function () {
+ progressText.textContent = 'Erreur réseau';
+ if (submitBtn) submitBtn.disabled = false;
+ });
+
+ xhr.addEventListener('abort', function () {
+ progressWrap.style.display = 'none';
+ if (submitBtn) submitBtn.disabled = false;
+ });
+
+ xhr.open('POST', form.action, true);
+ xhr.send(fd);
+ });
+ }
+})();
diff --git a/app/src/Controllers/ThesisCreateController.php b/app/src/Controllers/ThesisCreateController.php
index fb98971..42d8fb2 100644
--- a/app/src/Controllers/ThesisCreateController.php
+++ b/app/src/Controllers/ThesisCreateController.php
@@ -614,6 +614,7 @@ class ThesisCreateController
$result = PeerTubeService::upload(
$this->db,
$uploads['tmp_name'][$i],
+ $uploads['name'][$i],
$title,
''
);
diff --git a/app/src/Controllers/ThesisEditController.php b/app/src/Controllers/ThesisEditController.php
index fd8e589..fe05fdf 100644
--- a/app/src/Controllers/ThesisEditController.php
+++ b/app/src/Controllers/ThesisEditController.php
@@ -604,6 +604,7 @@ class ThesisEditController
$result = PeerTubeService::upload(
$this->db,
$uploads['tmp_name'][$i],
+ $uploads['name'][$i],
$title,
''
);
diff --git a/app/src/PeerTubeService.php b/app/src/PeerTubeService.php
index d451ddc..07f9970 100644
--- a/app/src/PeerTubeService.php
+++ b/app/src/PeerTubeService.php
@@ -17,11 +17,8 @@
* instance's /api/v1/oauth-clients/local endpoint and cached in-memory
* per process lifetime.
*
- * Upload uses the Google-resumable protocol:
- * POST /api/v1/videos/upload-resumable — init (→ Location header with upload URL token)
- * PATCH — send chunk
- * HEAD — resume check
- * DELETE — cancel
+ * Upload uses the simple multipart upload API:
+ * POST /api/v1/videos/upload — multipart form with CURLFile
*/
class PeerTubeService
{
@@ -157,14 +154,16 @@ class PeerTubeService
// -------------------------------------------------------------------------
/**
- * Upload a local file to PeerTube using the resumable upload protocol.
+ * Upload a local file to PeerTube using the simple multipart upload API.
*
+ * @param string $originalName The original client filename (e.g. "video.mp4") sent in the upload form.
* @return array{uuid:string, watchUrl:string}
* @throws \RuntimeException
*/
public static function upload(
Database $db,
string $filePath,
+ string $originalName,
string $title,
string $description = ''
): array {
@@ -180,106 +179,43 @@ class PeerTubeService
$token = self::obtainToken($s);
$baseUrl = $s['instance_url'];
- $fileSize = filesize($filePath);
- $fileName = basename($filePath);
$mimeType = (new \finfo(FILEINFO_MIME_TYPE))->file($filePath);
- // ── Step 1: Initialize resumable upload ──
- $initUrl = $baseUrl . '/api/v1/videos/upload-resumable';
- $initData = [
- 'channelId' => $channelId,
- 'name' => $title,
- 'privacy' => (int)$s['privacy'],
- 'waitTranscoding' => false,
- 'filename' => $fileName,
+ // ── Simple multipart upload (non-resumable) ──
+ $uploadUrl = $baseUrl . '/api/v1/videos/upload';
+
+ $postFields = [
+ 'channelId' => $channelId,
+ 'name' => $title,
+ 'privacy' => (int)$s['privacy'],
+ 'commentsEnabled' => true,
+ 'category' => 15,
+ 'videofile' => new \CURLFile($filePath, $mimeType, $originalName),
];
if ($description !== '') {
- $initData['description'] = $description;
+ $postFields['description'] = $description;
}
- $initBody = json_encode($initData);
- $initResponse = self::httpRequest($initUrl, 'POST', $initBody, [
+ $resp = self::httpRequest($uploadUrl, 'POST', $postFields, [
'Authorization: Bearer ' . $token,
- 'Content-Type: application/json',
- 'X-Upload-Content-Length: ' . $fileSize,
- 'X-Upload-Content-Type: ' . $mimeType,
- 'Content-Length: ' . strlen($initBody),
- ]);
+ ], 600);
- // PeerTube Google-resumable returns the upload session URL in the Location header.
- // The JSON body contains video.id (upload session ID, not final video ID).
- $chunkUrl = $initResponse['headers']['location'] ?? $initResponse['headers']['Location'] ?? null;
- if (!$chunkUrl) {
- $initJson = json_decode($initResponse['body'], true);
- $msg = $initJson['error'] ?? $initJson['detail'] ?? $initResponse['body'];
- throw new \RuntimeException('PeerTube upload init: no Location header (' . $initResponse['status'] . '): ' . $msg);
+ if ($resp['status'] < 200 || $resp['status'] >= 300) {
+ $errJson = json_decode($resp['body'], true);
+ $msg = $errJson['error'] ?? $errJson['detail'] ?? $resp['body'];
+ error_log('PeerTubeService: simple upload FAILED | status=' . $resp['status'] . ' | body=' . substr($resp['body'], 0, 500));
+ throw new \RuntimeException('PeerTube upload failed (' . $resp['status'] . '): ' . $msg);
}
- // Relative Location? Make it absolute.
- if (!str_starts_with($chunkUrl, 'http')) {
- $chunkUrl = rtrim($baseUrl, '/') . $chunkUrl;
- }
-
- // ── Step 2: Send chunks via PATCH (Google-resumable variant) ──
- $fh = fopen($filePath, 'rb');
- if (!$fh) {
- throw new \RuntimeException('Cannot open file for resumable upload.');
- }
-
- // Chunk size: 1 MB, must be a multiple of 256 KB (262144 bytes).
- $chunkSizeBase = 256 * 1024;
- $chunkSize = max($chunkSizeBase, min(4 * 1024 * 1024, (int)ceil($fileSize / 100)));
- $chunkSize = (int)ceil($chunkSize / $chunkSizeBase) * $chunkSizeBase;
-
- $offset = 0;
- $lastResponse = null;
-
- while ($offset < $fileSize) {
- $chunk = fread($fh, $chunkSize);
- $chunkLen = strlen($chunk);
- $end = $offset + $chunkLen - 1;
-
- $resp = self::httpRequest($chunkUrl, 'PATCH', $chunk, [
- 'Authorization: Bearer ' . $token,
- 'Content-Type: ' . $mimeType,
- 'Content-Range: bytes ' . $offset . '-' . $end . '/' . $fileSize,
- 'Content-Length: ' . $chunkLen,
- ], 600);
-
- $offset += $chunkLen;
-
- if ($resp['status'] >= 200 && $resp['status'] < 300) {
- $json = json_decode($resp['body'], true);
- if (isset($json['video']['shortUUID']) || isset($json['video']['uuid'])) {
- $lastResponse = $resp;
- break;
- }
- } elseif ($resp['status'] === 308) {
- // Resume Incomplete — chunk accepted, continue
- continue;
- } else {
- fclose($fh);
- try {
- self::cancelUpload($chunkUrl, $token);
- } catch (\Throwable $e) { /* ignore */ }
- $errJson = json_decode($resp['body'], true);
- $msg = $errJson['error'] ?? $errJson['detail'] ?? $resp['body'];
- throw new \RuntimeException('PeerTube chunk upload failed (' . $resp['status'] . '): ' . $msg);
- }
- }
- fclose($fh);
-
- if (!$lastResponse) {
- throw new \RuntimeException('PeerTube upload: no completion response.');
- }
-
- $finalJson = json_decode($lastResponse['body'], true);
- $shortUuid = $finalJson['video']['shortUUID'] ?? $finalJson['video']['uuid'] ?? null;
+ $json = json_decode($resp['body'], true);
+ $shortUuid = $json['video']['shortUUID'] ?? $json['video']['uuid'] ?? null;
if ($shortUuid === null) {
+ error_log('PeerTubeService: simple upload OK but no UUID | body=' . substr($resp['body'], 0, 500));
throw new \RuntimeException('PeerTube upload: no video UUID in response.');
}
$watchUrl = rtrim($baseUrl, '/') . '/videos/watch/' . $shortUuid;
+ error_log('PeerTubeService: simple upload OK | uuid=' . $shortUuid . ' | watchUrl=' . $watchUrl);
return ['uuid' => $shortUuid, 'watchUrl' => $watchUrl];
}
@@ -434,17 +370,6 @@ class PeerTubeService
// HTTP helper
// -------------------------------------------------------------------------
- /**
- * Cancel a resumable upload session.
- */
- private static function cancelUpload(string $chunkUrl, string $token): void
- {
- self::httpRequest($chunkUrl, 'DELETE', '', [
- 'Authorization: Bearer ' . $token,
- 'Content-Length: 0',
- ], 10);
- }
-
// -------------------------------------------------------------------------
// HTTP helper
// -------------------------------------------------------------------------
@@ -457,7 +382,7 @@ class PeerTubeService
public static function httpRequest(
string $url,
string $method,
- string $body,
+ string|array $body,
array $headers,
int $timeout = 300
): array {
@@ -475,6 +400,7 @@ class PeerTubeService
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_HTTPHEADER => $headers,
+ CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
CURLOPT_HEADERFUNCTION => function ($ch, $headerLine) use (&$responseHeaders) {
$len = strlen($headerLine);
$parts = explode(':', $headerLine, 2);
@@ -490,11 +416,8 @@ class PeerTubeService
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
- } elseif ($method === 'PUT') {
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
- curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
- } elseif ($method === 'PATCH') {
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
+ } elseif ($method === 'PUT' || $method === 'PATCH') {
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
} elseif ($method === 'DELETE') {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
diff --git a/app/templates/admin/acces.php b/app/templates/admin/acces.php
index 52a63cb..10776ad 100644
--- a/app/templates/admin/acces.php
+++ b/app/templates/admin/acces.php
@@ -870,6 +870,149 @@
+%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+\\\\\\\ to: usxlqwxk 3cd56fd1 "Cleanup acces fichier section" (rebased revision)
++ $linkName = $link['name'] ?? '';
+++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: usxlqwxk 3cd56fd1 "Cleanup acces fichier section" (rebased revision)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+- $linkName = $link['name'] ?? '';
+- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: kkxkwlvw 4b864504 "fix PeerTube upload: use Google-resumable protocol (Location header + PATCH) instead of PUT; capture response headers; ensure chunk size multiple of 256KB" (rebased revision)
+ $linkName = $link['name'] ?? '';
+ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+ $linkLockedYear = $link['locked_year'] ?? null;
++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
++\\\\\\\ to: kkxkwlvw 20cace79 "fix PeerTube upload: use Google-resumable protocol (Location header + PATCH) instead of PUT; capture response headers; ensure chunk size multiple of 256KB" (rebased revision)
+++ $linkName = $link['name'] ?? '';
+++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: kkxkwlvw 20cace79 "fix PeerTube upload: use Google-resumable protocol (Location header + PATCH) instead of PUT; capture response headers; ensure chunk size multiple of 256KB" (rebased revision)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+- $linkName = $link['name'] ?? '';
+- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: yxkvwkqy 7d620fae "fix PeerTube upload: Google-resumable (Location header, PATCH), +debug logging" (rebased revision)
+ $linkName = $link['name'] ?? '';
+ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+ $linkLockedYear = $link['locked_year'] ?? null;
++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
++\\\\\\\ to: yxkvwkqy 466cfa11 "fix PeerTube upload: Google-resumable (Location header, PATCH), +debug logging" (rebased revision)
+++ $linkName = $link['name'] ?? '';
+++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: yxkvwkqy 466cfa11 "fix PeerTube upload: Google-resumable (Location header, PATCH), +debug logging" (rebased revision)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+- $linkName = $link['name'] ?? '';
+- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: nnoxlkll cf5fe6f1 "fix PeerTube upload: add CURLOPT_VERBOSE debug; use BINARYTRANSFER for chunk body" (rebased revision)
+ $linkName = $link['name'] ?? '';
+ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+ $linkLockedYear = $link['locked_year'] ?? null;
++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
++\\\\\\\ to: nnoxlkll b2638810 "fix PeerTube upload: add CURLOPT_VERBOSE debug; use BINARYTRANSFER for chunk body" (rebased revision)
+++ $linkName = $link['name'] ?? '';
+++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: nnoxlkll b2638810 "fix PeerTube upload: add CURLOPT_VERBOSE debug; use BINARYTRANSFER for chunk body" (rebased revision)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+- $linkName = $link['name'] ?? '';
+- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: mosupsso 847f81fa "fix PeerTube upload: force HTTP/1.1 for PATCH chunks; remove deprecated CURLOPT_BINARYTRANSFER" (rebased revision)
+ $linkName = $link['name'] ?? '';
+ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+ $linkLockedYear = $link['locked_year'] ?? null;
++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
++\\\\\\\ to: mosupsso 09189e3c "fix PeerTube upload: force HTTP/1.1 for PATCH chunks; remove deprecated CURLOPT_BINARYTRANSFER" (rebased revision)
+++ $linkName = $link['name'] ?? '';
+++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: mosupsso 09189e3c "fix PeerTube upload: force HTTP/1.1 for PATCH chunks; remove deprecated CURLOPT_BINARYTRANSFER" (rebased revision)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+- $linkName = $link['name'] ?? '';
+- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: luxqovts 06e8aa29 "debug PeerTube PATCH 400: force HTTP/1.1, set CURLOPT_INFILESIZE, log verbose tail" (rebased revision)
+ $linkName = $link['name'] ?? '';
+ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+ $linkLockedYear = $link['locked_year'] ?? null;
++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
++\\\\\\\ to: luxqovts 6a770dd2 "debug PeerTube PATCH 400: force HTTP/1.1, set CURLOPT_INFILESIZE, log verbose tail" (rebased revision)
+++ $linkName = $link['name'] ?? '';
+++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: luxqovts 6a770dd2 "debug PeerTube PATCH 400: force HTTP/1.1, set CURLOPT_INFILESIZE, log verbose tail" (rebased revision)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+- $linkName = $link['name'] ?? '';
+- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: ollozskx ca8bf096 "debug PeerTube PATCH 400: let curl negotiate HTTP version, log raw hex body" (rebased revision)
+ $linkName = $link['name'] ?? '';
+ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+ $linkLockedYear = $link['locked_year'] ?? null;
++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
++\\\\\\\ to: ollozskx 162d5582 "debug PeerTube PATCH 400: let curl negotiate HTTP version, log raw hex body" (rebased revision)
+++ $linkName = $link['name'] ?? '';
+++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: ollozskx 162d5582 "debug PeerTube PATCH 400: let curl negotiate HTTP version, log raw hex body" (rebased revision)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+- $linkName = $link['name'] ?? '';
+- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: mxqulkkl c9d770c3 "fix PeerTube PATCH: force HTTP/2 (CURL_HTTP_VERSION_2_0) to match init connection" (rebased revision)
+ $linkName = $link['name'] ?? '';
+ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+ $linkLockedYear = $link['locked_year'] ?? null;
++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
++\\\\\\\ to: mxqulkkl 824b06f3 "fix PeerTube PATCH: force HTTP/2 (CURL_HTTP_VERSION_2_0) to match init connection" (rebased revision)
+++ $linkName = $link['name'] ?? '';
+++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: mxqulkkl 824b06f3 "fix PeerTube PATCH: force HTTP/2 (CURL_HTTP_VERSION_2_0) to match init connection" (rebased revision)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+- $linkName = $link['name'] ?? '';
+- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: nllmqxnz 3df60725 "fix PeerTube PATCH: use CURLOPT_INFILE stream for binary body; global CURL_HTTP_VERSION_2_0; fix chunkNum off-by-one" (rebased revision)
+ $linkName = $link['name'] ?? '';
+ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+ $linkLockedYear = $link['locked_year'] ?? null;
++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
++\\\\\\\ to: nllmqxnz 878a474a "fix PeerTube PATCH: use CURLOPT_INFILE stream for binary body; global CURL_HTTP_VERSION_2_0; fix chunkNum off-by-one" (rebased revision)
+++ $linkName = $link['name'] ?? '';
+++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: nllmqxnz 878a474a "fix PeerTube PATCH: use CURLOPT_INFILE stream for binary body; global CURL_HTTP_VERSION_2_0; fix chunkNum off-by-one" (rebased revision)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+- $linkName = $link['name'] ?? '';
+- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: usskovxu 2e5b566f "fix PeerTube init: remove waitTranscoding, add category+commentsEnabled; switch PATCH back to POSTFIELDS; remove verbose logging; clean curl_close" (rebased revision)
+ $linkName = $link['name'] ?? '';
+ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+ $linkLockedYear = $link['locked_year'] ?? null;
++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
++\\\\\\\ to: usskovxu de675701 "fix PeerTube init: remove waitTranscoding, add category+commentsEnabled; switch PATCH back to POSTFIELDS; remove verbose logging; clean curl_close" (rebased revision)
+++ $linkName = $link['name'] ?? '';
+++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: usskovxu de675701 "fix PeerTube init: remove waitTranscoding, add category+commentsEnabled; switch PATCH back to POSTFIELDS; remove verbose logging; clean curl_close" (rebased revision)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+- $linkName = $link['name'] ?? '';
+- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: surwmkqz e65faf60 "fix PeerTube upload: pass original filename in init body; chunk Content-Type → application/octet-stream" (rebased revision)
+ $linkName = $link['name'] ?? '';
+ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+ $linkLockedYear = $link['locked_year'] ?? null;
++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
++\\\\\\\ to: surwmkqz 0f7475bc "fix PeerTube upload: pass original filename in init body; chunk Content-Type → application/octet-stream" (rebased revision)
+++ $linkName = $link['name'] ?? '';
+++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: surwmkqz 0f7475bc "fix PeerTube upload: pass original filename in init body; chunk Content-Type → application/octet-stream" (rebased revision)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+- $linkName = $link['name'] ?? '';
+- $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: somsyvxz 14a3cd10 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebase destination)
+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ to: txzqmwnx 52b729bd "fix PeerTube upload: switch to simple multipart POST /api/v1/videos/upload with CURLFile; remove resumable protocol" (rebased revision)
+ $linkName = $link['name'] ?? '';
+ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
+ $linkLockedYear = $link['locked_year'] ?? null;
++%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
++\\\\\\\ to: txzqmwnx fe4b8d24 "fix PeerTube upload: switch to simple multipart POST /api/v1/videos/upload with CURLFile; remove resumable protocol" (rebased revision)
+++ $linkName = $link['name'] ?? '';
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
?>
diff --git a/app/templates/partials/form/form.php b/app/templates/partials/form/form.php
index f55717e..bd7a022 100644
--- a/app/templates/partials/form/form.php
+++ b/app/templates/partials/form/form.php
@@ -143,7 +143,7 @@ $checkedFormatsForSiteWeb = $checkedFormatsForSiteWeb ?? [];
-