render_music_template_candidates.ps1 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. param(
  2. [string]$CandidatesPath = "artifacts\music-template-extraction\candidates.json",
  3. [string]$OutputDir = "artifacts\music-template-extraction\review"
  4. )
  5. $ErrorActionPreference = "Stop"
  6. $data = Get-Content -Raw -Encoding UTF8 -LiteralPath $CandidatesPath | ConvertFrom-Json
  7. $probe = $null
  8. try {
  9. $probe = New-Object -ComObject KWPP.Application
  10. try { $probe.Quit() } catch {}
  11. } catch {
  12. $fallback = Join-Path $PSScriptRoot "render_music_template_candidates.py"
  13. $python = Get-Command python -ErrorAction Stop
  14. & $python.Source $fallback $CandidatesPath $OutputDir
  15. exit $LASTEXITCODE
  16. } finally {
  17. if ($probe) { [Runtime.InteropServices.Marshal]::ReleaseComObject($probe) | Out-Null }
  18. }
  19. $quotas = @{ child = 7; general = 12; traditional = 7; activity = 7; stage = 7; theory = 7 }
  20. $seenNames = @{}
  21. $seenSignatures = @{}
  22. $selected = [System.Collections.Generic.List[object]]::new()
  23. foreach ($scene in $quotas.Keys) {
  24. $count = 0
  25. $items = $data.items | Where-Object {
  26. $_.scene -eq $scene -and $_.score -ge 78 -and $_.slides -ge 10 -and $_.slides -le 60 -and
  27. $_.size_mb -le 250 -and $_.median_text -le 120
  28. } | Sort-Object score -Descending
  29. foreach ($item in $items) {
  30. if ($count -ge $quotas[$scene]) { break }
  31. if ($seenNames.ContainsKey($item.name_key) -or $seenSignatures.ContainsKey($item.signature)) { continue }
  32. $seenNames[$item.name_key] = $true
  33. $seenSignatures[$item.signature] = $true
  34. $selected.Add($item)
  35. $count += 1
  36. }
  37. }
  38. New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
  39. $OutputDir = (Resolve-Path -LiteralPath $OutputDir).Path
  40. $rendered = [System.Collections.Generic.List[object]]::new()
  41. for ($candidateIndex = 0; $candidateIndex -lt $selected.Count; $candidateIndex += 1) {
  42. $item = $selected[$candidateIndex]
  43. $id = "candidate-{0:d2}" -f ($candidateIndex + 1)
  44. $candidateDir = Join-Path $OutputDir $id
  45. New-Item -ItemType Directory -Force -Path $candidateDir | Out-Null
  46. $app = $null
  47. $deck = $null
  48. try {
  49. $app = New-Object -ComObject KWPP.Application
  50. try { $app.Visible = $false } catch {}
  51. $deck = $app.Presentations.Open($item.path, $true, $true, $false)
  52. $slideCount = $deck.Slides.Count
  53. $indexes = @(1, [int][math]::Max(2, [math]::Round($slideCount * 0.25)), [int][math]::Max(2, [math]::Round($slideCount * 0.5)), [int][math]::Max(2, [math]::Round($slideCount * 0.75)), [int]$slideCount) | Sort-Object -Unique
  54. foreach ($slideIndex in $indexes) {
  55. $slideIndex = [int]$slideIndex
  56. $target = Join-Path $candidateDir ("slide-{0:d2}.jpg" -f $slideIndex)
  57. $deck.Slides.Item($slideIndex).Export($target, "JPG", 640, 360)
  58. }
  59. $rendered.Add([pscustomobject]@{
  60. id = $id
  61. name = $item.name
  62. scene = $item.scene
  63. score = $item.score
  64. source = $item.path
  65. slides = $slideCount
  66. samples = @($indexes | ForEach-Object { "slide-{0:d2}.jpg" -f $_ })
  67. })
  68. Write-Host "$id`t$($item.scene)`t$($item.name)"
  69. }
  70. catch {
  71. Write-Warning "$id render failed: $($_.Exception.Message)"
  72. }
  73. finally {
  74. if ($null -ne $deck) { try { $deck.Close() } catch {} }
  75. if ($null -ne $app) { try { $app.Quit() } catch {} }
  76. if ($null -ne $deck) { [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($deck) | Out-Null }
  77. if ($null -ne $app) { [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) | Out-Null }
  78. [gc]::Collect()
  79. [gc]::WaitForPendingFinalizers()
  80. }
  81. }
  82. @{ items = $rendered } | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $OutputDir "manifest.json") -Encoding UTF8