197 lines
8.0 KiB
PowerShell
197 lines
8.0 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Full recursive mirror of BABZ Bund ILIAS course content.
|
|
Downloads all SCORM modules and PDFs found by the Python crawler.
|
|
|
|
.NOTES
|
|
Requires PHPSESSID in ~/.env
|
|
Requires crawl_results.txt generated by the Python crawler.
|
|
Run from the C:\GitHub\FW directory.
|
|
#>
|
|
|
|
$EnvFilePath = Join-Path $env:USERPROFILE ".env"
|
|
$PHPSESSID = $null
|
|
if (Test-Path $EnvFilePath) {
|
|
Get-Content $EnvFilePath | Where-Object { $_ -match "^PHPSESSID=(.*)" } | ForEach-Object {
|
|
$PHPSESSID = $Matches[1].Trim()
|
|
}
|
|
}
|
|
|
|
if ([string]::IsNullOrWhiteSpace($PHPSESSID)) {
|
|
Write-Error "PHPSESSID is missing! Please add it to your ~/.env file."
|
|
exit 1
|
|
}
|
|
|
|
$BASE_ILIAS = "https://lernplattform-babz-bund.de"
|
|
$BASE_DATA = "$BASE_ILIAS/data/BBKILIAS/lm_data"
|
|
$OUT_DIR = $PSScriptRoot # C:\GitHub\FW
|
|
|
|
$Headers = @{ "Cookie" = "PHPSESSID=$PHPSESSID" }
|
|
|
|
$Modules = [ordered]@{
|
|
"Chemische Kampfstoffe_final" = 159348
|
|
"WBT Motorola Digitalfunk_final" = 0 # HTLM-Format, wird separat behandelt (kein lm_id)
|
|
"Ionenmobilitaetsspektrometer" = 167131
|
|
"Photoionisationsdetektor" = 161852
|
|
"MGMG Alarme und Sonderzustaende" = 167132
|
|
"MGMG Gase und Messprinzipien" = 182052
|
|
"MGMG Material" = 167134
|
|
"MGMG Menue" = 167135
|
|
"MGMG Wartung" = 167136
|
|
"CoMo-170 ZS-2 (KNG)" = 167139
|
|
"RadEye (DLM)" = 167137
|
|
"WBT_ErkW_1_Einfuehrung" = 167175
|
|
"WBT_ErkW_2_Aufbau_und_Ueberblick_Bedienoberflaeche" = 167176
|
|
"WBT_ErkW_3_Einsatzbereitschaft_herstellen" = 167177
|
|
"WBT_ErkW_4_Aufgesessene_Messung" = 167178
|
|
"WBT_ErkW_5_Abgesetzte_Messung" = 167179
|
|
"WBT_ErkW_6_Administration_erw_Funktionen" = 167180
|
|
"Grundlagen_radiologische_Formeln" = 167618
|
|
}
|
|
|
|
$PDFs = [ordered]@{}
|
|
|
|
function Invoke-FileDownload {
|
|
param([string]$Url, [string]$Dest)
|
|
$dir = Split-Path $Dest -Parent
|
|
if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
|
|
try {
|
|
$wc = New-Object System.Net.WebClient
|
|
$wc.Headers.Add("Cookie", "PHPSESSID=$PHPSESSID")
|
|
$wc.DownloadFile($Url, $Dest)
|
|
return $true
|
|
} catch {
|
|
Write-Warning " FAILED: $Url ($_)"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
Write-Host "`n========================================" -ForegroundColor Cyan
|
|
Write-Host " SCORM MODULES ($($Modules.Count) total)" -ForegroundColor Cyan
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
|
|
$totalFiles = 0
|
|
$failedFiles = 0
|
|
|
|
foreach ($ModuleName in $Modules.Keys) {
|
|
$lmId = $Modules[$ModuleName]
|
|
$baseUrl = "$BASE_DATA/lm_$lmId"
|
|
$destDir = Join-Path $OUT_DIR "Trainings\$ModuleName"
|
|
|
|
Write-Host "`n[Module] $ModuleName (lm_$lmId)" -ForegroundColor Yellow
|
|
|
|
# -- Core entry points every Storyline module has --
|
|
$CoreFiles = @(
|
|
"story.html", "index_lms.html", "analytics-frame.html", "imsmanifest.xml", "meta.xml",
|
|
"html5/data/css/output.min.css", "html5/data/js/data.js", "html5/data/js/frame.js",
|
|
"html5/data/js/paths.js", "html5/lib/scripts/bootstrapper.min.js",
|
|
"html5/lib/scripts/frame.desktop.min.js", "html5/lib/scripts/frame.mobile.min.js",
|
|
"html5/lib/scripts/slides.min.js", "story_content/user.js", "story_content/triggers.js",
|
|
"story_content/thumbnail.jpg", "mobile/fonts.json"
|
|
)
|
|
|
|
$downloaded = 0
|
|
$failed = 0
|
|
|
|
foreach ($f in $CoreFiles) {
|
|
$url = "$baseUrl/$f"
|
|
$dest = Join-Path $destDir ($f -replace '/', '\')
|
|
$ok = Invoke-FileDownload -Url $url -Dest $dest
|
|
if ($ok) { $downloaded++ } else { $failed++ }
|
|
}
|
|
|
|
$manifestPath = Join-Path $destDir "imsmanifest.xml"
|
|
if (Test-Path $manifestPath) {
|
|
$xmlContent = (Get-Content $manifestPath -Raw -Encoding UTF8) -replace "`x1A", ""
|
|
[xml]$manifest = $xmlContent
|
|
$ns = @{ ims = "http://www.imsproject.org/xsd/imscp_rootv1p1p2" }
|
|
$fileNodes = $manifest.SelectNodes("//file", $null) + $manifest.SelectNodes("//*[local-name()='file']")
|
|
$manifestFiles = $fileNodes | ForEach-Object { $_.href } | Where-Object { $_ } | Sort-Object -Unique
|
|
|
|
foreach ($f in $manifestFiles) {
|
|
$f = $f -replace '\\', '/'
|
|
$url = "$baseUrl/$f"
|
|
$dest = Join-Path $destDir ($f -replace '/', '\')
|
|
if (-not (Test-Path $dest)) {
|
|
$ok = Invoke-FileDownload -Url $url -Dest $dest
|
|
if ($ok) { $downloaded++ } else { $failed++ }
|
|
}
|
|
}
|
|
}
|
|
|
|
$dataJsPath = Join-Path $destDir "html5\data\js\data.js"
|
|
if (Test-Path $dataJsPath) {
|
|
$dataContent = Get-Content $dataJsPath -Raw
|
|
$chunkRefs = [regex]::Matches($dataContent, '"([A-Za-z0-9_-]{8,15}\.js)"') | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique
|
|
if ($chunkRefs.Count -gt 0) {
|
|
foreach ($chunk in $chunkRefs) {
|
|
$url = "$baseUrl/html5/data/js/$chunk"
|
|
$dest = Join-Path $destDir "html5\data\js\$chunk"
|
|
if (-not (Test-Path $dest)) {
|
|
$ok = Invoke-FileDownload -Url $url -Dest $dest
|
|
if ($ok) { $downloaded++ } else { $failed++ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$pathsJsPath = Join-Path $destDir "html5\data\js\paths.js"
|
|
if (Test-Path $pathsJsPath) {
|
|
$pathsContent = Get-Content $pathsJsPath -Raw
|
|
$audioRefs = [regex]::Matches($pathsContent, '"([A-Za-z0-9_-]{8,20}_\d+_\d+_\d+\.mp3)"') | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique
|
|
if ($audioRefs.Count -gt 0) {
|
|
foreach ($audio in $audioRefs) {
|
|
$url = "$baseUrl/story_content/$audio"
|
|
$dest = Join-Path $destDir "story_content\$audio"
|
|
if (-not (Test-Path $dest)) {
|
|
$ok = Invoke-FileDownload -Url $url -Dest $dest
|
|
if ($ok) { $downloaded++ } else { $failed++ }
|
|
}
|
|
}
|
|
}
|
|
$pngRefs = [regex]::Matches($pathsContent, '"([A-Za-z0-9_-]{8,20}\.png)"') | ForEach-Object { $_.Groups[1].Value } | Sort-Object -Unique
|
|
foreach ($png in $pngRefs) {
|
|
$url = "$baseUrl/mobile/$png"
|
|
$dest = Join-Path $destDir "mobile\$png"
|
|
if (-not (Test-Path $dest)) {
|
|
$ok = Invoke-FileDownload -Url $url -Dest $dest
|
|
if ($ok) { $downloaded++ } else { $failed++ }
|
|
}
|
|
}
|
|
}
|
|
|
|
$totalFiles += $downloaded
|
|
$failedFiles += $failed
|
|
$fc = if ($failed -eq 0) { 'Green' } else { 'Red' }
|
|
Write-Host " OK $downloaded new files downloaded, $failed failed" -ForegroundColor $fc
|
|
}
|
|
|
|
Write-Host "`n========================================" -ForegroundColor Cyan
|
|
Write-Host " PDF DOCUMENTS ($($PDFs.Count) total)" -ForegroundColor Cyan
|
|
Write-Host "========================================" -ForegroundColor Cyan
|
|
|
|
$pdfDir = Join-Path $OUT_DIR "Documents"
|
|
if (-not (Test-Path $pdfDir)) { New-Item -ItemType Directory -Path $pdfDir -Force | Out-Null }
|
|
|
|
foreach ($FileName in $PDFs.Keys) {
|
|
$refId = $PDFs[$FileName]
|
|
$url = "$BASE_ILIAS/goto.php/file/$refId/download"
|
|
$dest = Join-Path $pdfDir $FileName
|
|
|
|
Write-Host " [PDF] $FileName (ref_id=$refId)" -ForegroundColor Yellow
|
|
if ((Invoke-FileDownload -Url $url -Dest $dest) -and (Test-Path $dest)) {
|
|
$size = (Get-Item $dest).Length
|
|
$header = [System.Text.Encoding]::ASCII.GetString((Get-Content $dest -Encoding Byte -TotalCount 4))
|
|
if ($header -ne "%PDF") {
|
|
Write-Warning " ! $FileName does not appear to be a valid PDF (size: $size, header: $header)"
|
|
$failedFiles++
|
|
} else {
|
|
Write-Host " OK $([Math]::Round($size/1KB, 1)) KB" -ForegroundColor Green
|
|
$totalFiles++
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-Host "`nDone!" -ForegroundColor Green
|
|
|