ObsiViewer/test_obsidian-excalidraw.ps1

208 lines
5.5 KiB
PowerShell

<#
.SYNOPSIS
Décompresse la section ```compressed-json``` d'un fichier .excalidraw.md (Obsidian/Excalidraw).
Utilise Node.js + npm pour accéder à la librairie LZ-String officielle.
.PARAMETER Path
Chemin du .excalidraw.md
.PARAMETER OutFile
Fichier JSON de sortie (optionnel). Par défaut: <Path>.decompressed.json
#>
param(
[Parameter(Mandatory=$true)]
[string]$Path,
[string]$OutFile
)
if (-not (Test-Path -LiteralPath $Path)) {
Write-Host "❌ Fichier introuvable: $Path" -ForegroundColor Red
exit 1
}
# --- Lecture et extraction du bloc compressed-json --------------------------
$content = Get-Content -LiteralPath $Path -Raw
$encoded = $null
if ($content -match '```compressed-json\s*([\s\S]*?)\s*```') {
$encoded = $matches[1]
} elseif ($content -match '%%\s*compressed-json\s*([\s\S]*?)\s*%%') {
$encoded = $matches[1]
} else {
Write-Host "⚠️ Aucune section compressed-json trouvée (ni fenced ``` ni %%)." -ForegroundColor Yellow
exit 2
}
$encoded = ($encoded -replace '[^A-Za-z0-9\+\/\=]', '').Trim()
if (-not $encoded) {
Write-Host "⚠️ Section compressed-json vide après nettoyage." -ForegroundColor Yellow
exit 3
}
if (-not $OutFile) {
$OutFile = [System.IO.Path]::ChangeExtension($Path, ".decompressed.json")
}
# --- Vérifier Node.js ---
$node = Get-Command node -ErrorAction SilentlyContinue
if (-not $node) {
Write-Host "❌ Node.js n'est pas installé ou non trouvé dans PATH." -ForegroundColor Red
Write-Host " Installez Node.js depuis https://nodejs.org/" -ForegroundColor Yellow
exit 5
}
# --- Créer un fichier temporaire Node.js ---
$tempDir = [System.IO.Path]::GetTempPath()
$tempScript = Join-Path $tempDir "decompress_lz.js"
$tempOutput = Join-Path $tempDir "lz_output.json"
# Créer le script Node.js
$nodeScript = @"
// Décompression LZ-String manuelle (compatible avec compressToBase64)
const keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
function decompressFromBase64(input) {
if (!input) return "";
// Décodage base64 vers tableau d'octets
let bytes = [];
for (let i = 0; i < input.length; i += 4) {
let b1 = keyStrBase64.indexOf(input[i]) || 0;
let b2 = keyStrBase64.indexOf(input[i + 1]) || 0;
let b3 = keyStrBase64.indexOf(input[i + 2]) || 0;
let b4 = keyStrBase64.indexOf(input[i + 3]) || 0;
bytes.push((b1 << 2) | (b2 >> 4));
if (b3 < 64) bytes.push(((b2 & 15) << 4) | (b3 >> 2));
if (b4 < 64) bytes.push(((b3 & 3) << 6) | b4);
}
// Conversion en UTF16 (caractères)
let compressed = "";
for (let i = 0; i < bytes.length; i += 2) {
let c = bytes[i] | (bytes[i + 1] << 8 || 0);
compressed += String.fromCharCode(c);
}
return decompressFromUTF16(compressed);
}
function decompressFromUTF16(compressed) {
if (!compressed) return "";
let dict = [];
let dictSize = 4;
let numBits = 3;
let dataIdx = 0;
let bitPos = 0;
function readBits(n) {
let result = 0;
for (let i = 0; i < n; i++) {
if (bitPos >= compressed.length * 16) return result;
let charIdx = Math.floor(bitPos / 16);
let bitOffset = bitPos % 16;
let bit = (compressed.charCodeAt(charIdx) >> bitOffset) & 1;
result |= (bit << i);
bitPos++;
}
return result;
}
// Lire le premier code
let c = readBits(2);
if (c === 0) {
let val = readBits(8);
dict.push(String.fromCharCode(val));
} else if (c === 1) {
let val = readBits(16);
dict.push(String.fromCharCode(val));
} else if (c === 2) {
return "";
}
let w = dict[dict.length - 1];
let result = w;
while (true) {
c = readBits(numBits);
if (c === 0) {
let val = readBits(8);
dict.push(String.fromCharCode(val));
c = dict.length - 1;
} else if (c === 1) {
let val = readBits(16);
dict.push(String.fromCharCode(val));
c = dict.length - 1;
} else if (c === 2) {
return result;
}
let entry;
if (c < dict.length) {
entry = dict[c];
} else if (c === dict.length) {
entry = w + w[0];
} else {
return null;
}
result += entry;
if (dict.length < 65536) {
dict.push(w + entry[0]);
}
if (dict.length >= (1 << numBits)) {
numBits++;
}
w = entry;
}
}
try {
const input = process.argv[2];
const output = decompressFromBase64(input);
const fs = require('fs');
fs.writeFileSync(process.argv[3], output, 'utf8');
console.log('OK');
} catch (err) {
console.error('ERROR: ' + err.message);
process.exit(1);
}
"@
$nodeScript | Out-File -FilePath $tempScript -Encoding utf8
try {
Write-Host "📦 Tentative de décompression..." -ForegroundColor Cyan
Write-Host " Base64 length: $($encoded.Length) caractères" -ForegroundColor Gray
# Exécuter le script Node.js en passant le base64 directement
$output = & node $tempScript $encoded $tempOutput 2>&1
if ($LASTEXITCODE -ne 0 -or $output -contains "ERROR") {
Write-Host "❌ Erreur de décompression: $output" -ForegroundColor Red
exit 4
}
if (Test-Path $tempOutput) {
Copy-Item $tempOutput $OutFile -Force
$json = Get-Content $tempOutput -Raw
Write-Host "✅ Décompression OK → $OutFile" -ForegroundColor Green
Write-Host " Taille JSON: $($json.Length) caractères" -ForegroundColor Gray
} else {
Write-Host "❌ Le fichier de sortie n'a pas été créé." -ForegroundColor Red
exit 4
}
} catch {
Write-Host "❌ Erreur lors de l'exécution: $_" -ForegroundColor Red
exit 4
} finally {
# Nettoyage
Remove-Item $tempScript -ErrorAction SilentlyContinue
Remove-Item $tempOutput -ErrorAction SilentlyContinue
}