205 lines
7.8 KiB
PowerShell
205 lines
7.8 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/babz/lm_data"
|
|
$OUT_DIR = $PSScriptRoot # C:\GitHub\FW
|
|
|
|
$Headers = @{ "Cookie" = "PHPSESSID=$PHPSESSID" }
|
|
|
|
# Read crawl_results.txt
|
|
$CrawlResults = Join-Path $OUT_DIR "crawl_results.txt"
|
|
if (-not (Test-Path $CrawlResults)) {
|
|
Write-Error "crawl_results.txt not found. Please run the python crawler first."
|
|
exit 1
|
|
}
|
|
|
|
$Modules = [ordered]@{}
|
|
$PDFs = [ordered]@{}
|
|
|
|
$currentSection = ""
|
|
Get-Content $CrawlResults | ForEach-Object {
|
|
$line = $_.Trim()
|
|
if ($line -match "^Modules") { $currentSection = "Modules" }
|
|
elseif ($line -match "^PDFs") { $currentSection = "PDFs" }
|
|
elseif ($line -match "^(.+?)\s+\|\s+(.+)$") {
|
|
$title = $Matches[1] -replace '[<>:"/\\|?*]', '_'
|
|
$url = $Matches[2]
|
|
if ($currentSection -eq "Modules") {
|
|
if ($url -match "lm_(\d+)") {
|
|
$Modules[$title] = $Matches[1]
|
|
} elseif ($url -match "ref_id=(\d+)") {
|
|
$Modules[$title] = $Matches[1]
|
|
}
|
|
} elseif ($currentSection -eq "PDFs") {
|
|
if ($url -match "file/(\d+)/download") {
|
|
$PDFs["$title.pdf"] = $Matches[1]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|