refactor: merge video/audio FilePond pools into TFE input

- Remove separate video/audio/peertube_video/peertube_audio pools from UI
- TFE pool now accepts all file types including video/audio
- When PeerTube is enabled, video/audio dropped into TFE pool auto-upload
  to PeerTube (process.php detects MIME and uploads immediately)
- PeerTube return IDs now encode type: peertube:video:UUID or peertube:audio:UUID
- load.php returns placeholder SVG for PeerTube files so they appear in FilePond
- Edit mode: all existing files (including PeerTube) shown in TFE FilePond pool
- Remove legacy  video/audio/peertube_* handling from both controllers
- Remove unused vide/audio/peertube_* entries from JS QUEUE_CONFIG
This commit is contained in:
Pontoporeia
2026-05-12 12:08:51 +02:00
parent 1ff3c70ebe
commit 6e7c0c00e3
11 changed files with 89 additions and 201 deletions

12
TODO.md
View File

@@ -1,4 +1,14 @@
# FilePond Server-ID Refactor # FilePond Refactor — Merge video/audio into TFE pool
- [x] A. `fichiers-fragment.php` — Remove separate video/audio pools, merge into TFE; include PeerTube in data-existing-files
- [x] B. `file-upload-filepond.js` — Remove peertube_video/peertube_audio/video/audio from QUEUE_CONFIG, remove acceptedFileTypesPeerTube, remove data-peertube-active logic
- [x] C. `process.php` — When queue_type=tfe and video/audio + PeerTube enabled, upload to PeerTube, return peertube:UUID
- [x] D. `load.php` — Handle peertube DB files: return placeholder SVG blob
- [x] E. `form.php` — Include PeerTube files in existingFilesJsonForTfe for edit mode
- [x] F. `ThesisEditController.php` — Remove separate video/audio/peertube_* handleFilePondQueueFiles calls; also legacy $_FILES path
- [x] G. `ThesisCreateController.php` — Same as F
## Previous items
- [x] Step 1 — Build 4 PHP endpoints (process.php, revert.php, load.php, remove.php) - [x] Step 1 — Build 4 PHP endpoints (process.php, revert.php, load.php, remove.php)
- [x] Step 2 — Update ThesisFileHandler to accept file_ids instead of $_FILES - [x] Step 2 — Update ThesisFileHandler to accept file_ids instead of $_FILES

View File

@@ -45,10 +45,19 @@ $filePath = $fileRow['file_path'] ?? '';
$fileName = $fileRow['file_name'] ?? basename($filePath); $fileName = $fileRow['file_name'] ?? basename($filePath);
$mimeType = $fileRow['mime_type'] ?? 'application/octet-stream'; $mimeType = $fileRow['mime_type'] ?? 'application/octet-stream';
// ── Skip PeerTube and website entries (no actual file) ─────────────────── // ── PeerTube entries: return a placeholder SVG blob so FilePond can display them ─┐
if (str_starts_with($filePath, 'peertube_ids:')) { if (str_starts_with($filePath, 'peertube_ids:')) {
http_response_code(404); $uuid = substr($filePath, strlen('peertube_ids:'));
die('Fichier PeerTube — pas de flux direct.'); $isVideo = ($fileRow['file_type'] ?? '') === 'video';
$svg = $isVideo
? '<svg xmlns="http://www.w3.org/2000/svg" width="180" height="120" viewBox="0 0 180 120"><rect width="180" height="120" fill="#1a1a2e"/><polygon points="70,35 70,85 125,60" fill="#e94560"/><text x="90" y="110" text-anchor="middle" font-family="sans-serif" font-size="10" fill="#aaa">PeerTube ' . htmlspecialchars($uuid) . '</text></svg>'
: '<svg xmlns="http://www.w3.org/2000/svg" width="180" height="120" viewBox="0 0 180 120"><rect width="180" height="120" fill="#1a1a2e"/><circle cx="55" cy="60" r="20" fill="none" stroke="#4ecca3" stroke-width="3"/><line x1="72" y1="48" x2="95" y2="38" stroke="#4ecca3" stroke-width="3"/><line x1="72" y1="60" x2="110" y2="60" stroke="#4ecca3" stroke-width="3"/><line x1="72" y1="72" x2="95" y2="82" stroke="#4ecca3" stroke-width="3"/><text x="90" y="110" text-anchor="middle" font-family="sans-serif" font-size="10" fill="#aaa">PeerTube ' . htmlspecialchars($uuid) . '</text></svg>';
header('Content-Type: image/svg+xml');
header('Content-Length: ' . strlen($svg));
header('Content-Disposition: inline; filename="peertube.svg"');
header('Cache-Control: no-cache');
echo $svg;
exit;
} }
if (str_starts_with($filePath, 'http://') || str_starts_with($filePath, 'https://')) { if (str_starts_with($filePath, 'http://') || str_starts_with($filePath, 'https://')) {
http_response_code(404); http_response_code(404);

View File

@@ -209,11 +209,14 @@ chmod($targetPath, 0644);
error_log('[filepond:process] File saved to tmp | file_id=' . $fileId . ' | path=' . $targetPath); error_log('[filepond:process] File saved to tmp | file_id=' . $fileId . ' | path=' . $targetPath);
// ── PeerTube: upload immediately (don't wait for form submit) ──────────── // ── PeerTube: upload immediately (don't wait for form submit) ────────────
$isPeerTube = str_starts_with($queueType, 'peertube_'); // Handles both dedicated peertube_* queues (legacy) and video/audio in the TFE pool
if ($isPeerTube) { $isPeerTubeQueue = str_starts_with($queueType, 'peertube_');
$isTfeAv = ($queueType === 'tfe' && preg_match('/^(video|audio)\//', $mimeType));
$shouldPeerTube = $isPeerTubeQueue || $isTfeAv;
if ($shouldPeerTube) {
require_once APP_ROOT . '/src/PeerTubeService.php'; require_once APP_ROOT . '/src/PeerTubeService.php';
if (PeerTubeService::isEnabled(new Database())) { if (PeerTubeService::isEnabled(new Database())) {
$ptFileType = ($queueType === 'peertube_video') ? 'video' : 'audio'; $ptFileType = preg_match('/^video\//', $mimeType) ? 'video' : 'audio';
try { try {
$result = PeerTubeService::upload( $result = PeerTubeService::upload(
new Database(), new Database(),
@@ -223,7 +226,8 @@ if ($isPeerTube) {
'' ''
); );
// Return a special ID prefix so the controller knows not to look in tmp/ // Return a special ID prefix so the controller knows not to look in tmp/
$fileId = 'peertube:' . $result['uuid']; // Format: peertube:video:UUID or peertube:audio:UUID
$fileId = 'peertube:' . $ptFileType . ':' . $result['uuid'];
// Clean up temp file — PeerTube has its own copy now // Clean up temp file — PeerTube has its own copy now
@unlink($targetPath); @unlink($targetPath);
@rmdir($tmpDir); @rmdir($tmpDir);
@@ -239,12 +243,15 @@ if ($isPeerTube) {
die('Erreur lors du téléversement vers PeerTube.'); die('Erreur lors du téléversement vers PeerTube.');
} }
} else { } else {
// PeerTube not enabled — reject the upload // PeerTube not enabled — save to disk normally (only for tfe pool, not dedicated peertube queues)
if ($isPeerTubeQueue) {
@unlink($targetPath); @unlink($targetPath);
@rmdir($tmpDir); @rmdir($tmpDir);
http_response_code(503); http_response_code(503);
die('PeerTube n\'est pas activé.'); die('PeerTube n\'est pas activé.');
} }
// For TFE pool, fall through to normal disk save below
}
} }
// ── Write manifest ─────────────────────────────────────────────────────── // ── Write manifest ───────────────────────────────────────────────────────

View File

@@ -31,6 +31,7 @@ if ($_SERVER['REQUEST_METHOD'] !== 'DELETE') {
$fileId = trim(file_get_contents('php://input')); $fileId = trim(file_get_contents('php://input'));
// PeerTube files have a special prefix; nothing to clean up locally // PeerTube files have a special prefix; nothing to clean up locally
// Format: peertube:video:UUID or peertube:audio:UUID
if (str_starts_with($fileId, 'peertube:')) { if (str_starts_with($fileId, 'peertube:')) {
// PeerTube files are already uploaded; we don't delete them from PeerTube on revert // PeerTube files are already uploaded; we don't delete them from PeerTube on revert
// (the user might still submit and associate them) // (the user might still submit and associate them)

View File

@@ -33,16 +33,8 @@
"text/vtt", "text/vtt",
"application/zip", "application/x-tar", "application/gzip" "application/zip", "application/x-tar", "application/gzip"
], ],
// When PeerTube is active, exclude video/audio from TFE pool
acceptedFileTypesPeerTube: [
"image/jpeg", "image/png", "image/gif", "image/webp",
"application/pdf",
"text/vtt",
"application/zip", "application/x-tar", "application/gzip"
],
labelFileTypeNotAllowed: "Format non accepté", labelFileTypeNotAllowed: "Format non accepté",
fileValidateTypeLabelExpectedTypes: "PDF, Images, Vidéos, Audio, VTT, Archives", fileValidateTypeLabelExpectedTypes: "PDF, Images, Vidéos, Audio, VTT, Archives",
fileValidateTypeLabelExpectedTypesPeerTube: "PDF, Images, VTT, Archives",
maxFileSize: "500MB", maxFileSize: "500MB",
labelMaxFileSizeExceeded: "Fichier trop volumineux", labelMaxFileSizeExceeded: "Fichier trop volumineux",
labelMaxFileSize: "Taille max: {filesize}", labelMaxFileSize: "Taille max: {filesize}",
@@ -54,24 +46,6 @@
mp3: "2GB", ogg: "2GB", oga: "2GB", wav: "2GB", flac: "2GB", aac: "2GB", m4a: "2GB" mp3: "2GB", ogg: "2GB", oga: "2GB", wav: "2GB", flac: "2GB", aac: "2GB", m4a: "2GB"
} }
}, },
video: {
acceptedFileTypes: ["video/mp4", "video/webm", "video/ogg", "video/quicktime"],
labelFileTypeNotAllowed: "Format non accepté",
fileValidateTypeLabelExpectedTypes: "MP4, WebM, OGV, MOV",
maxFileSize: "500MB",
labelMaxFileSizeExceeded: "Fichier trop volumineux",
labelMaxFileSize: "Taille max: {filesize}",
allowMultiple: true
},
audio: {
acceptedFileTypes: ["audio/mpeg", "audio/ogg", "audio/flac", "audio/x-wav", "audio/aac", "audio/mp4"],
labelFileTypeNotAllowed: "Format non accepté",
fileValidateTypeLabelExpectedTypes: "MP3, OGG, FLAC, WAV, AAC, M4A",
maxFileSize: "500MB",
labelMaxFileSizeExceeded: "Fichier trop volumineux",
labelMaxFileSize: "Taille max: {filesize}",
allowMultiple: true
},
annexe: { annexe: {
acceptedFileTypes: ["application/pdf", "application/zip", "application/x-tar", "application/gzip"], acceptedFileTypes: ["application/pdf", "application/zip", "application/x-tar", "application/gzip"],
labelFileTypeNotAllowed: "Format non accepté", labelFileTypeNotAllowed: "Format non accepté",
@@ -99,24 +73,6 @@
labelMaxFileSize: "Taille max: {filesize}", labelMaxFileSize: "Taille max: {filesize}",
allowMultiple: false allowMultiple: false
}, },
peertube_video: {
acceptedFileTypes: ["video/mp4", "video/webm", "video/ogg", "video/quicktime"],
labelFileTypeNotAllowed: "Format non accepté",
fileValidateTypeLabelExpectedTypes: "MP4, WebM, OGV, MOV",
maxFileSize: "500MB",
labelMaxFileSizeExceeded: "Fichier trop volumineux",
labelMaxFileSize: "Taille max: {filesize}",
allowMultiple: true
},
peertube_audio: {
acceptedFileTypes: ["audio/mpeg", "audio/ogg", "audio/flac", "audio/x-wav", "audio/aac", "audio/mp4"],
labelFileTypeNotAllowed: "Format non accepté",
fileValidateTypeLabelExpectedTypes: "MP3, OGG, FLAC, WAV, AAC, M4A",
maxFileSize: "500MB",
labelMaxFileSizeExceeded: "Fichier trop volumineux",
labelMaxFileSize: "Taille max: {filesize}",
allowMultiple: true
},
}; };
// ── Helpers ─────────────────────────────────────────────────────────── // ── Helpers ───────────────────────────────────────────────────────────
@@ -270,15 +226,6 @@
// Per-type max size overrides (for TFE: PDF=100MB, video/audio=2GB) // Per-type max size overrides (for TFE: PDF=100MB, video/audio=2GB)
var perExtMax = cfg.perExtensionMaxSize || {}; var perExtMax = cfg.perExtensionMaxSize || {};
// When PeerTube is active, restrict TFE pool to PDF/text only
var peerTubeActive = queueType === "tfe" && input.dataset.peertubeActive === "1";
var acceptedFileTypes = peerTubeActive && cfg.acceptedFileTypesPeerTube
? cfg.acceptedFileTypesPeerTube
: cfg.acceptedFileTypes;
var expectedTypesLabel = peerTubeActive && cfg.fileValidateTypeLabelExpectedTypesPeerTube
? cfg.fileValidateTypeLabelExpectedTypesPeerTube
: cfg.fileValidateTypeLabelExpectedTypes;
return { return {
allowMultiple: cfg.allowMultiple, allowMultiple: cfg.allowMultiple,
allowReorder: true, allowReorder: true,
@@ -287,9 +234,9 @@
server: buildServerConfig(queueType), server: buildServerConfig(queueType),
// ── Native FilePond validation ── // ── Native FilePond validation ──
acceptedFileTypes: acceptedFileTypes, acceptedFileTypes: cfg.acceptedFileTypes,
labelFileTypeNotAllowed: cfg.labelFileTypeNotAllowed, labelFileTypeNotAllowed: cfg.labelFileTypeNotAllowed,
fileValidateTypeLabelExpectedTypes: expectedTypesLabel, fileValidateTypeLabelExpectedTypes: cfg.fileValidateTypeLabelExpectedTypes,
maxFileSize: cfg.maxFileSize, maxFileSize: cfg.maxFileSize,
labelMaxFileSizeExceeded: cfg.labelMaxFileSizeExceeded, labelMaxFileSizeExceeded: cfg.labelMaxFileSizeExceeded,
labelMaxFileSize: cfg.labelMaxFileSize, labelMaxFileSize: cfg.labelMaxFileSize,

View File

@@ -213,7 +213,7 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
</div> </div>
</div> </div>
<!-- ── 3. TFE ── --> <!-- ── 3. TFE (all files: PDF, images, video, audio, VTT, archives) ── -->
<div class="admin-form-group admin-files-fieldgroup"> <div class="admin-form-group admin-files-fieldgroup">
<label for="tfe-files-input">TFE<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></label> <label for="tfe-files-input">TFE<?= !$adminMode ? ' <span class="asterisk">*</span>' : '' ?></label>
<div class="admin-file-input"> <div class="admin-file-input">
@@ -223,17 +223,14 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
class="tfe-file-picker" class="tfe-file-picker"
data-queue-type="tfe" data-queue-type="tfe"
<?= !$adminMode ? 'required' : '' ?> <?= !$adminMode ? 'required' : '' ?>
data-peertube-active="<?= $peerTubeEnabled ? '1' : '0' ?>"
data-existing-files='<?= htmlspecialchars(json_encode($existingFilesJsonForTfe ?? []), ENT_QUOTES) ?>'> data-existing-files='<?= htmlspecialchars(json_encode($existingFilesJsonForTfe ?? []), ENT_QUOTES) ?>'>
<small class="admin-file-hint"> <small class="admin-file-hint">
PDF (max 100 MB) · Images (max 500 MB) · Vidéo &amp; Audio (max 2 GB) · VTT · Archives (max 500 MB).
<br>Glissez pour réordonner.
<?php if ($peerTubeEnabled): ?> <?php if ($peerTubeEnabled): ?>
PDF (max 100 MB) · Images (max 500 MB) · VTT · Archives (max 500 MB). <br>Vidéos et audio hébergés sur <a href="<?= htmlspecialchars($peerTubeSettings['instance_url']) ?>" target="_blank" rel="noopener">PeerTube</a>.
Vidéo & Audio → utilisez les emplacements dédiés ci-dessous.
<?php else: ?>
PDF (max 100 MB) · Images (max 500 MB) · Vidéo & Audio (max 2 GB) · VTT · Archives (max 500 MB).
<?php endif; ?> <?php endif; ?>
Glissez pour réordonner. <br>PDFs trop lourds ? <a href="https://www.bentopdf.com" target="_blank" rel="noopener">https://bentopdf.com/</a>
PDFs trop lourds ? <a href="https://www.bentopdf.com" target="_blank" rel="noopener">https://bentopdf.com/</a>
</small> </small>
</div> </div>
</div> </div>
@@ -254,65 +251,7 @@ $websiteLabel = htmlspecialchars($_POST['website_label'] ?? '');
</div> </div>
</div> </div>
<!-- ── 5. Vidéo / PeerTube ── --> <!-- ── 5. Site web url ── -->
<?php if ($peerTubeEnabled): ?>
<div id="slot-video" class="admin-form-group admin-files-fieldgroup">
<label for="peertube-video-input">Vidéo (optionnel)</label>
<div class="admin-file-input">
<input type="file" id="peertube-video-input"
name="queue_file[peertube_video][]"
multiple
accept="video/mp4,video/webm,video/ogg,video/quicktime,.mp4,.webm,.ogv,.mov"
class="tfe-file-picker"
data-queue-type="peertube_video">
<small class="admin-file-hint">MP4, WebM ou MOV. Max 500 MB. Hébergé sur <a href="<?= htmlspecialchars($peerTubeSettings['instance_url']) ?>" target="_blank" rel="noopener">PeerTube</a>.</small>
</div>
</div>
<?php else: ?>
<div id="slot-video" class="admin-form-group admin-files-fieldgroup">
<label for="video-files-input">Vidéo (optionnel)</label>
<div class="admin-file-input">
<input type="file" id="video-files-input"
name="queue_file[video][]"
multiple
accept="video/mp4,video/webm,video/ogg,video/quicktime,.mp4,.webm,.ogv,.mov"
class="tfe-file-picker"
data-queue-type="video">
<small class="admin-file-hint">MP4, WebM ou MOV. Max 500 MB.</small>
</div>
</div>
<?php endif; ?>
<!-- ── 6. Audio / PeerTube ── -->
<?php if ($peerTubeEnabled): ?>
<div id="slot-audio" class="admin-form-group admin-files-fieldgroup">
<label for="peertube-audio-input">Audio (optionnel)</label>
<div class="admin-file-input">
<input type="file" id="peertube-audio-input"
name="queue_file[peertube_audio][]"
multiple
accept="audio/mpeg,audio/ogg,audio/wav,audio/flac,audio/aac,audio/mp4,.mp3,.ogg,.oga,.wav,.flac,.aac,.m4a"
class="tfe-file-picker"
data-queue-type="peertube_audio">
<small class="admin-file-hint">MP3, OGG, WAV, FLAC ou AAC. Max 500 MB. Hébergé sur <a href="<?= htmlspecialchars($peerTubeSettings['instance_url']) ?>" target="_blank" rel="noopener">PeerTube</a>.</small>
</div>
</div>
<?php else: ?>
<div id="slot-audio" class="admin-form-group admin-files-fieldgroup">
<label for="audio-files-input">Audio (optionnel)</label>
<div class="admin-file-input">
<input type="file" id="audio-files-input"
name="queue_file[audio][]"
multiple
accept="audio/mpeg,audio/ogg,audio/wav,audio/flac,audio/aac,audio/mp4,.mp3,.ogg,.oga,.wav,.flac,.aac,.m4a"
class="tfe-file-picker"
data-queue-type="audio">
<small class="admin-file-hint">MP3, OGG, WAV, FLAC ou AAC. Max 500 MB.</small>
</div>
</div>
<?php endif; ?>
<!-- ── 7. Site web url ── -->
<div id="slot-siteweb" class="admin-form-group"> <div id="slot-siteweb" class="admin-form-group">
<label for="website_url">URL du site (optionnel)</label> <label for="website_url">URL du site (optionnel)</label>
<div class="admin-file-input"> <div class="admin-file-input">

View File

@@ -200,31 +200,17 @@ class ThesisCreateController
$this->handleFilePondSingleFile($thesisId, $post, 'cover', $folderPath, $filePrefix); $this->handleFilePondSingleFile($thesisId, $post, 'cover', $folderPath, $filePrefix);
$this->handleFilePondSingleFile($thesisId, $post, 'note_intention', $folderPath, $filePrefix); $this->handleFilePondSingleFile($thesisId, $post, 'note_intention', $folderPath, $filePrefix);
$nextNum = $this->handleFilePondQueueFiles($thesisId, $post, 'tfe', $folderPath, $filePrefix, 1); $nextNum = $this->handleFilePondQueueFiles($thesisId, $post, 'tfe', $folderPath, $filePrefix, 1);
$nextNum = $this->handleFilePondQueueFiles($thesisId, $post, 'video', $folderPath, $filePrefix, $nextNum);
$nextNum = $this->handleFilePondQueueFiles($thesisId, $post, 'audio', $folderPath, $filePrefix, $nextNum);
$this->handleFilePondQueueFiles($thesisId, $post, 'annexe', $folderPath, $filePrefix, 0); $this->handleFilePondQueueFiles($thesisId, $post, 'annexe', $folderPath, $filePrefix, 0);
$this->handleFilePondQueueFiles($thesisId, $post, 'peertube_video', $folderPath, $filePrefix, 0, null);
$this->handleFilePondQueueFiles($thesisId, $post, 'peertube_audio', $folderPath, $filePrefix, 0, null);
} else { } else {
// Legacy path: files arrive via multipart $_FILES // Legacy path: files arrive via multipart $_FILES
$this->handleCoverUpload($thesisId, $files['couverture'] ?? null, $folderPath, $filePrefix); $this->handleCoverUpload($thesisId, $files['couverture'] ?? null, $folderPath, $filePrefix);
$this->handleNoteIntentionUpload($thesisId, $files['note_intention'] ?? null, $folderPath, $filePrefix); $this->handleNoteIntentionUpload($thesisId, $files['note_intention'] ?? null, $folderPath, $filePrefix);
$queueFiles = $files['queue_file'] ?? []; $queueFiles = $files['queue_file'] ?? [];
$qTfe = $this->extractFilesSubArray($queueFiles, 'tfe'); $qTfe = $this->extractFilesSubArray($queueFiles, 'tfe');
$qVideo = $this->extractFilesSubArray($queueFiles, 'video');
$qAudio = $this->extractFilesSubArray($queueFiles, 'audio');
$qAnnexe = $this->extractFilesSubArray($queueFiles, 'annexe'); $qAnnexe = $this->extractFilesSubArray($queueFiles, 'annexe');
$nextNum = $this->handleTfeQueueFiles($thesisId, $qTfe, $folderPath, $filePrefix, 1); $nextNum = $this->handleTfeQueueFiles($thesisId, $qTfe, $folderPath, $filePrefix, 1);
$nextNum = $this->handleTfeQueueFiles($thesisId, $qVideo, $folderPath, $filePrefix, $nextNum);
$nextNum = $this->handleTfeQueueFiles($thesisId, $qAudio, $folderPath, $filePrefix, $nextNum);
$this->handleAnnexeQueueFiles($thesisId, $qAnnexe, $folderPath, $filePrefix); $this->handleAnnexeQueueFiles($thesisId, $qAnnexe, $folderPath, $filePrefix);
// ── 5b. PeerTube video / audio uploads (from FilePond queue) ──────────
$qPTVideo = $this->extractFilesSubArray($queueFiles, 'peertube_video');
$qPTAudio = $this->extractFilesSubArray($queueFiles, 'peertube_audio');
$this->handlePeerTubeQueueFiles($thesisId, $data['titre'], $qPTVideo, 'video');
$this->handlePeerTubeQueueFiles($thesisId, $data['titre'], $qPTAudio, 'audio');
} }
// ── 6. Website URL — stored as thesis_files row ────────────────────── // ── 6. Website URL — stored as thesis_files row ──────────────────────

View File

@@ -464,35 +464,21 @@ class ThesisEditController
// New path: files already on server via async FilePond uploads // New path: files already on server via async FilePond uploads
$nextNum = $tfeCount + 1; $nextNum = $tfeCount + 1;
$nextNum = $this->handleFilePondQueueFiles($thesisId, $post, 'tfe', $folderPath, $filePrefix, $nextNum); $nextNum = $this->handleFilePondQueueFiles($thesisId, $post, 'tfe', $folderPath, $filePrefix, $nextNum);
$nextNum = $this->handleFilePondQueueFiles($thesisId, $post, 'video', $folderPath, $filePrefix, $nextNum);
$this->handleFilePondQueueFiles($thesisId, $post, 'audio', $folderPath, $filePrefix, $nextNum);
$this->handleFilePondQueueFiles($thesisId, $post, 'annexe', $folderPath, $filePrefix, 0); $this->handleFilePondQueueFiles($thesisId, $post, 'annexe', $folderPath, $filePrefix, 0);
$this->handleFilePondQueueFiles($thesisId, $post, 'peertube_video', $folderPath, $filePrefix, 0, $progressToken);
$this->handleFilePondQueueFiles($thesisId, $post, 'peertube_audio', $folderPath, $filePrefix, 0, $progressToken);
} else { } else {
// Legacy path: files arrive via multipart $_FILES // Legacy path: files arrive via multipart $_FILES
$queueFiles = $files['queue_file'] ?? []; $queueFiles = $files['queue_file'] ?? [];
$qTfe = $this->extractFilesSubArray($queueFiles, 'tfe'); $qTfe = $this->extractFilesSubArray($queueFiles, 'tfe');
$qVideo = $this->extractFilesSubArray($queueFiles, 'video');
$qAudio = $this->extractFilesSubArray($queueFiles, 'audio');
$qAnnexe = $this->extractFilesSubArray($queueFiles, 'annexe'); $qAnnexe = $this->extractFilesSubArray($queueFiles, 'annexe');
$startNum = $tfeCount + 1; $startNum = $tfeCount + 1;
$startNum = $this->handleTfeQueueFiles($thesisId, $qTfe, $folderPath, $filePrefix, $startNum); $startNum = $this->handleTfeQueueFiles($thesisId, $qTfe, $folderPath, $filePrefix, $startNum);
$startNum = $this->handleTfeQueueFiles($thesisId, $qVideo, $folderPath, $filePrefix, $startNum);
$this->handleTfeQueueFiles($thesisId, $qAudio, $folderPath, $filePrefix, $startNum);
$this->handleAnnexeQueueFiles($thesisId, $qAnnexe, $folderPath, $filePrefix); $this->handleAnnexeQueueFiles($thesisId, $qAnnexe, $folderPath, $filePrefix);
// Legacy annexe files (direct upload, non-queue path — kept for backwards compat) // Legacy annexe files (direct upload, non-queue path — kept for backwards compat)
if (isset($files['annexes']) && is_array($files['annexes']['name'] ?? null)) { if (isset($files['annexes']) && is_array($files['annexes']['name'] ?? null)) {
$this->handleAnnexeFiles($thesisId, $files['annexes'], $folderPath, $filePrefix, $post); $this->handleAnnexeFiles($thesisId, $files['annexes'], $folderPath, $filePrefix, $post);
} }
// ── PeerTube video / audio uploads (from FilePond queue) ──────────────
$qPTVideo = $this->extractFilesSubArray($queueFiles, 'peertube_video');
$qPTAudio = $this->extractFilesSubArray($queueFiles, 'peertube_audio');
$this->handlePeerTubeQueueFiles($thesisId, trim($post['titre'] ?? ''), $qPTVideo, 'video', $progressToken);
$this->handlePeerTubeQueueFiles($thesisId, trim($post['titre'] ?? ''), $qPTAudio, 'audio', $progressToken);
} }
// ── Website URL — add or update ────────────────────────────────────── // ── Website URL — add or update ──────────────────────────────────────

View File

@@ -1001,16 +1001,18 @@ trait ThesisFileHandler
} }
// PeerTube files have been uploaded already; just insert DB row // PeerTube files have been uploaded already; just insert DB row
// Format: peertube:video:UUID or peertube:audio:UUID
if (str_starts_with($fileId, 'peertube:')) { if (str_starts_with($fileId, 'peertube:')) {
$uuid = substr($fileId, strlen('peertube:')); $parts = explode(':', $fileId, 3);
$fileType = ($queueKey === 'peertube_video') ? 'video' : 'audio'; $fileType = ($parts[1] ?? '') === 'video' ? 'video' : 'audio';
$uuid = $parts[2] ?? '';
$storedPath = 'peertube_ids:' . $uuid; $storedPath = 'peertube_ids:' . $uuid;
$this->db->insertThesisFile( $this->db->insertThesisFile(
$thesisId, $fileType, $thesisId, $fileType,
$storedPath, $storedPath,
$uuid . ' (PeerTube)', $uuid . ' (PeerTube)',
0, 0,
($queueKey === 'peertube_video') ? 'video/mp4' : 'audio/mpeg', $fileType === 'video' ? 'video/mp4' : 'audio/mpeg',
null, null null, null
); );
error_log("ThesisFileHandler: PeerTube file associated → $uuid"); error_log("ThesisFileHandler: PeerTube file associated → $uuid");

View File

@@ -1559,6 +1559,19 @@
+%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision) +%%%%%%% diff from: somsyvxz 249f7943 "Bulk bar anti-shift, tags icons, AP no-wrap, credits reorder" (rebased revision)
+\\\\\\\ to: uktvpzsn ce8e9ac6 "fix: track vendor JS files, add 'unsafe-inline' to public CSP, gitignore filepond tmp" (rebased revision) +\\\\\\\ to: uktvpzsn ce8e9ac6 "fix: track vendor JS files, add 'unsafe-inline' to public CSP, gitignore filepond tmp" (rebased revision)
++ $linkName = $link['name'] ?? ''; ++ $linkName = $link['name'] ?? '';
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff from: uktvpzsn ce8e9ac6 "fix: track vendor JS files, add 'unsafe-inline' to public CSP, gitignore filepond tmp" (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: tsultupz 3195eef4 "refactor: merge video/audio FilePond pools into TFE input" (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: tsultupz 0703279b "refactor: merge video/audio FilePond pools into TFE input" (rebased revision)
++ $linkName = $link['name'] ?? '';
++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : ''; ++ $linkExpiresVal = $link['expires_at'] ? date('Y-m-d\TH:i', strtotime($link['expires_at'])) : '';
?> ?>
<tr class="admin-table-row" onclick="event.stopPropagation(); window.open('/partage/<?= urlencode($link['slug']) ?>', '_blank')" style="cursor:pointer"> <tr class="admin-table-row" onclick="event.stopPropagation(); window.open('/partage/<?= urlencode($link['slug']) ?>', '_blank')" style="cursor:pointer">

View File

@@ -307,7 +307,26 @@ $checkedFormatsForSiteWeb = $checkedFormatsForSiteWeb ?? [];
?> ?>
<!-- ═══════════════════ Format(s) + Fichiers ═══════════════════ --> <!-- ═══════════════════ Format(s) + Fichiers ═══════════════════ -->
<?php if ($filesMode === 'add'): ?> <?php
// Helper: build existing-files JSON for FilePond TFE pool (including PeerTube)
$_buildExistingFilesJson = function (array $files): array {
$result = [];
foreach ($files as $f) {
$ft = $f['file_type'] ?? '';
$fp = $f['file_path'] ?? '';
// Skip cover (handled separately) and website URLs (no actual file)
if ($ft === 'cover' || str_starts_with($fp, 'http://') || str_starts_with($fp, 'https://')) {
continue;
}
// Include PeerTube files too — load.php now handles them
$result[] = [
'source' => (string)((int)$f['id']),
'options' => ['type' => 'local'],
];
}
return $result;
};
if ($filesMode === 'add'): ?>
<?php <?php
// Add / partage mode: Format + Fichiers rendered as one HTMX-swappable block. // Add / partage mode: Format + Fichiers rendered as one HTMX-swappable block.
// Synthesise POST-like data so fichiers-fragment.php can render the initial state. // Synthesise POST-like data so fichiers-fragment.php can render the initial state.
@@ -324,23 +343,7 @@ $checkedFormatsForSiteWeb = $checkedFormatsForSiteWeb ?? [];
$_POST['admin_mode'] = $adminMode ? '1' : '0'; $_POST['admin_mode'] = $adminMode ? '1' : '0';
$_POST['has_annexes'] = $formData['has_annexes'] ?? null; $_POST['has_annexes'] = $formData['has_annexes'] ?? null;
// Build existing-files JSON for FilePond edit mode $existingFilesJsonForTfe = $_buildExistingFilesJson($currentFiles ?? []);
$existingFilesJsonForTfe = [];
if (!empty($currentFiles)) {
foreach ($currentFiles as $f) {
$ft = $f['file_type'] ?? '';
$fp = $f['file_path'] ?? '';
// Skip cover (handled separately) and website/peertube (no actual file)
if ($ft === 'cover' || str_starts_with($fp, 'http://') || str_starts_with($fp, 'https://') || str_starts_with($fp, 'peertube_ids:')) {
continue;
}
// Only include files that can be streamed back via load.php
$existingFilesJsonForTfe[] = [
'source' => (string)((int)$f['id']),
'options' => ['type' => 'local'],
];
}
}
include APP_ROOT . '/public/partage/fichiers-fragment.php'; include APP_ROOT . '/public/partage/fichiers-fragment.php';
$_POST = $_savedPost; $_POST = $_savedPost;
@@ -359,23 +362,8 @@ $checkedFormatsForSiteWeb = $checkedFormatsForSiteWeb ?? [];
$_POST['_cover'] = $currentCover['file_path'] ?? null; $_POST['_cover'] = $currentCover['file_path'] ?? null;
$_POST['has_annexes'] = $formData['has_annexes'] ?? null; $_POST['has_annexes'] = $formData['has_annexes'] ?? null;
// Build existing-files JSON for FilePond edit mode // Build existing-files JSON for FilePond edit mode (all files including PeerTube)
$existingFilesJsonForTfe = []; $existingFilesJsonForTfe = $_buildExistingFilesJson($currentFiles ?? []);
if (!empty($currentFiles)) {
foreach ($currentFiles as $f) {
$ft = $f['file_type'] ?? '';
$fp = $f['file_path'] ?? '';
// Skip cover (handled separately) and website/peertube (no actual file)
if ($ft === 'cover' || str_starts_with($fp, 'http://') || str_starts_with($fp, 'https://') || str_starts_with($fp, 'peertube_ids:')) {
continue;
}
// Only include files that can be streamed back via load.php
$existingFilesJsonForTfe[] = [
'source' => (string)((int)$f['id']),
'options' => ['type' => 'local'],
];
}
}
include APP_ROOT . '/public/partage/fichiers-fragment.php'; include APP_ROOT . '/public/partage/fichiers-fragment.php';
$_POST = $_savedPost; $_POST = $_savedPost;