Pipeline de Traitement Multimédia & Déploiement Piwigo
Suite outillée pour l’ingestion, le découpage vidéo sans perte, la compression AV1/WebM, l’optimisation multithread WebP, le calcul d’empreintes cryptographiques, la pré-génération CLI des dérivés sous Laragon et la réplication cloud (Mega S4 & o2switch).
1. Vue d’ensemble du workflow
[Médias Bruts]
│
├──> [Découpage sans perte] ──> [Encodage SVT-AV1 / Opus] ──> [Rclone: Mega S4]
│
└──> [Extraction PNG] ──> [Multithread WebP] ──> [Génération Dump SQL MD5]
│
▼
[Mise en ligne o2switch] <── [Rclone dérivés] <── [Génération Dérivés CLI] <── [Import DB / Sync Albums]
2. Découpage vidéo sans perte par scène (FFmpeg CLI)
Permet d’extraire des segments temporels ciblés sans réencodage, de manière quasi instantanée et sans altérer la qualité originale.
ffmpeg -i input.mp4 -ss 00:03:40 -to 00:15:53 -c copy -avoid_negative_ts make_zero output_1.mp4TIP
Positionner
-ssaprès l’argument d’entrée-ipréserve la synchronisation précise sur les images clés (keyframes), tandis que-avoid_negative_ts make_zeroréinitialise l’horodatage initial pour éviter tout gel d’image.
3. Conversion vidéo par lot : SVT-AV1 & WebM
Script PowerShell de conversion vers le format AV1 / conteneur WebM avec tempo thermique.
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$outputDir = "Videos_AV1_WebM"
$preset = 8
$crf = 35
$pauseSeconds = 60
$files = Get-ChildItem -Path . -File | Where-Object { $_.Extension -match '^\.(mp4|mkv|mov)$' }
$total = $files.Count
if ($total -eq 0) {
Write-Host "Aucun fichier vidéo trouvé." -ForegroundColor Yellow
exit
}
if (!(Test-Path $outputDir)) {
New-Item -ItemType Directory -Path $outputDir | Out-Null
}
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
$currentIndex = 0
foreach ($file in $files) {
$currentIndex++
$outputFile = Join-Path $outputDir "$($file.BaseName)_compressed.webm"
if (Test-Path $outputFile) {
Write-Host " ⏭️ Ignoré : $($file.Name) existe déjà." -ForegroundColor Gray
continue
}
& ffmpeg -hide_banner -i "$($file.FullName)" `
-c:v libsvtav1 -preset $preset -crf $crf `
-c:a libopus -b:a 128k `
-y "$outputFile"
if ($currentIndex -lt $total) {
Start-Sleep -Seconds $pauseSeconds
}
}
$stopwatch.Stop()
[System.Console]::Beep(880, 500)4. Optimisation Multithread PNG vers WebP
Conversion haute performance via l’architecture RunspacePool avec suppression conditionnelle de la source.
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$baseDest = "_webp"
$extensionSource = "*.png"
$maxThreads = 12
$qualiteWebp = 90
$compressionWebp = 4
$LogFile = "D:\Pix\conversion.log"
$files = Get-ChildItem -Path . -Filter $extensionSource -File
$totalFichiers = $files.Count
if ($totalFichiers -eq 0) { exit }
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $maxThreads)
$RunspacePool.Open()
$Jobs = [System.Collections.Generic.List[object]]::new()
$index = 0
foreach ($file in $files) {
$index++
$partNum = [math]::Floor(($index - 1) / 1000) + 1
$targetFolder = Join-Path $baseDest "pt_$($partNum.ToString('00'))"
if (!(Test-Path -LiteralPath $targetFolder)) { New-Item -ItemType Directory -Path $targetFolder | Out-Null }
$targetPath = Join-Path $targetFolder "$($file.BaseName).webp"
$PowerShell = [powershell]::Create().AddScript({
param($src, $dst, $q, $c)
& ffmpeg -nostdin -i "$src" -vcodec libwebp -pix_fmt yuv420p -color_range 2 `
-preset default -q:v $q -compression_level $c -threads 1 -y -loglevel error "$dst"
if ((Test-Path -LiteralPath $dst) -and ((Get-Item -LiteralPath $dst).Length -gt 0)) {
Remove-Item -LiteralPath $src -Force
return [PSCustomObject]@{ Statut = "OK"; Source = $src }
}
return [PSCustomObject]@{ Statut = "ERREUR"; Source = $src }
}).AddArgument($file.FullName).AddArgument($targetPath).AddArgument($qualiteWebp).AddArgument($compressionWebp)
$PowerShell.RunspacePool = $RunspacePool
$Jobs.Add([PSCustomObject]@{ Pipe = $PowerShell; Result = $PowerShell.BeginInvoke() })
}
while ($Jobs.Count -gt 0) {
for ($j = $Jobs.Count - 1; $j -ge 0; $j--) {
if ($Jobs[$j].Result.IsCompleted) {
$taskReturn = $Jobs[$j].Pipe.EndInvoke($Jobs[$j].Result)
$Jobs[$j].Pipe.Dispose()
$Jobs.RemoveAt($j)
}
}
Start-Sleep -Milliseconds 100
}
$RunspacePool.Close()
$RunspacePool.Dispose()
[System.Console]::Beep(880, 500)5. Audit d’intégrité & Injection SQL MD5
Calcul en flux direct de l’empreinte MD5 pour insertion dans la table pwg_images de Piwigo :
$DossierLocal = "C:\Photos\MonAlbum"
$FichierSqlSortie = "C:\Photos\update_md5.sql"
"START TRANSACTION;" | Out-File -FilePath $FichierSqlSortie -Encoding UTF8
$Photos = Get-ChildItem -Path $DossierLocal -Include *.webp, *.jpg, *.png -Recurse -File
$md5 = [System.Security.Cryptography.MD5]::Create()
foreach ($Photo in $Photos) {
$stream = [System.IO.File]::OpenRead($Photo.FullName)
$hashBytes = $md5.ComputeHash($stream)
$stream.Close()
$stream.Dispose()
$HashString = (-join ($hashBytes | ForEach-Object { "{0:x2}" -f $_ }))
$NomFichier = $Photo.Name
$LigneSql = "UPDATE pwg_images SET md5sum = '$HashString' WHERE file = '$NomFichier' AND (md5sum IS NULL OR md5sum = '');"
$LigneSql | Out-File -FilePath $FichierSqlSortie -Append -Encoding UTF8
}
"COMMIT;" | Out-File -FilePath $FichierSqlSortie -Append -Encoding UTF86. Pré-génération locale des Dérivés (CLI Laragon)
Exécution dans le terminal Laragon pour épargner le processeur d’o2switch :
# Identifier l'album
php plugins/piwigo-cli/bin/pwg.php album list
# Générer les dérivés en multithread (-p ID, -j workers)
php plugins\piwigo-cli\bin\pwg.php photo generate-derivatives -p 24 -j 127. Synchronisation Distante (rclone)
Déploiement des Dérivés vers o2switch
rclone copy "D:\Websites\__LARAGON__\www\gallery\_data\i" o2switch.website:gallery.dakta.website/_data/i -P --transfers=8 --checkers=16Réplication Stockage Objet vers Mega S4
# Profils adaptés selon la typologie de fichiers :
# Video : --transfers=6 --checkers=8 --s3-chunk-size=32M --buffer-size=32M
# Images : --transfers=32 --checkers=64 --s3-chunk-size=5M --buffer-size=0M
& rclone.exe copy "D:\Videos\Scenes" "megas4:s4-vidz/Ffmpeg" --fast-list --s3-acl=public-read -P