diff --git a/generate-ventoy-json.ps1 b/generate-ventoy-json.ps1 new file mode 100644 index 0000000..01be1d2 --- /dev/null +++ b/generate-ventoy-json.ps1 @@ -0,0 +1,360 @@ +# generate-ventoy-json.ps1 +# Scans the iso-library directory tree and produces a ventoy.json menu structure. +# Usage: .\generate-ventoy-json.ps1 [-IsoRoot ] [-OutFile ] [-VentoyDrive ] + +param( + [string]$IsoRoot = ".\iso-library", + [string]$OutFile = ".\ventoy.json", + [string]$VentoyDrive = "E" # drive letter of your Ventoy USB +) + +# ── Friendly display names for OS canonical slugs ────────────────────────────── +$displayNames = @{ + 'windows' = 'Windows' + 'windows-server' = 'Windows Server' + 'ms-dos' = 'MS-DOS' + 'reactos' = 'ReactOS' + 'rhel' = 'Red Hat Enterprise Linux' + 'centos' = 'CentOS' + 'centos-stream' = 'CentOS Stream' + 'rocky' = 'Rocky Linux' + 'almalinux' = 'AlmaLinux' + 'oraclelinux' = 'Oracle Linux' + 'scientific' = 'Scientific Linux' + 'eurolinux' = 'EuroLinux' + 'fedora' = 'Fedora' + 'qubes' = 'Qubes OS' + 'opensuse-leap' = 'openSUSE Leap' + 'opensuse-tumbleweed' = 'openSUSE Tumbleweed' + 'opensuse-microos' = 'openSUSE MicroOS' + 'opensuse' = 'openSUSE' + 'sles' = 'SUSE Linux Enterprise' + 'debian' = 'Debian' + 'ubuntu' = 'Ubuntu' + 'ubuntu-mate' = 'Ubuntu MATE' + 'ubuntu-budgie' = 'Ubuntu Budgie' + 'ubuntu-kylin' = 'Ubuntu Kylin' + 'ubuntu-studio' = 'Ubuntu Studio' + 'ubuntu-unity' = 'Ubuntu Unity' + 'lubuntu' = 'Lubuntu' + 'kubuntu' = 'Kubuntu' + 'xubuntu' = 'Xubuntu' + 'linuxmint' = 'Linux Mint' + 'lmde' = 'Linux Mint Debian Edition' + 'pop_os' = 'Pop!_OS' + 'elementary' = 'elementary OS' + 'zorin' = 'Zorin OS' + 'kali' = 'Kali Linux' + 'parrot' = 'Parrot OS' + 'backbox' = 'BackBox' + 'tails' = 'Tails' + 'whonix' = 'Whonix' + 'raspios' = 'Raspberry Pi OS' + 'armbian' = 'Armbian' + 'devuan' = 'Devuan' + 'mx' = 'MX Linux' + 'antix' = 'antiX' + 'deepin' = 'Deepin' + 'pureos' = 'PureOS' + 'grml' = 'GRML' + 'libreelec' = 'LibreELEC' + 'arch' = 'Arch Linux' + 'manjaro' = 'Manjaro' + 'endeavouros' = 'EndeavourOS' + 'garuda' = 'Garuda Linux' + 'artix' = 'Artix Linux' + 'cachyos' = 'CachyOS' + 'gentoo' = 'Gentoo' + 'void' = 'Void Linux' + 'nixos' = 'NixOS' + 'alpine' = 'Alpine Linux' + 'solus' = 'Solus' + 'clearlinux' = 'Clear Linux' + 'flatcar' = 'Flatcar' + 'coreos' = 'Fedora CoreOS' + 'slackware' = 'Slackware' + 'pfsense' = 'pfSense' + 'opnsense' = 'OPNsense' + 'ipfire' = 'IPFire' + 'vyos' = 'VyOS' + 'openwrt' = 'OpenWrt' + 'proxmox-ve' = 'Proxmox VE' + 'proxmox-bs' = 'Proxmox Backup Server' + 'proxmox-mg' = 'Proxmox Mail Gateway' + 'esxi' = 'VMware ESXi' + 'vsphere' = 'VMware vSphere' + 'xcp-ng' = 'XCP-ng' + 'xenserver' = 'XenServer' + 'harvester' = 'Harvester' + 'freebsd' = 'FreeBSD' + 'openbsd' = 'OpenBSD' + 'netbsd' = 'NetBSD' + 'dragonflybsd' = 'DragonFly BSD' + 'truenas-scale' = 'TrueNAS SCALE' + 'truenas-core' = 'TrueNAS CORE' + 'truenas' = 'TrueNAS' + 'ghostbsd' = 'GhostBSD' + 'openindiana' = 'OpenIndiana' + 'omnios' = 'OmniOS' + 'smartos' = 'SmartOS' + 'haiku' = 'Haiku' + 'freedos' = 'FreeDOS' + 'clonezilla' = 'Clonezilla' + 'systemrescue' = 'SystemRescue' + 'gparted' = 'GParted Live' + 'memtest' = 'Memtest+' + 'memtest86' = 'Memtest86' + 'rescuezilla' = 'Rescuezilla' + 'hirens' = "Hiren's BootCD" + 'winpe' = 'WinPE' + 'dban' = 'DBAN' + 'kaspersky-rescue' = 'Kaspersky Rescue Disk' + 'bitdefender-rescue'= 'Bitdefender Rescue' + 'eset-rescue' = 'ESET SysRescue' +} + +# ── Category groupings — slug prefix -> menu group ──────────────────────────── +$categoryMap = [ordered]@{ + 'windows' = 'Windows' + 'windows-server'= 'Windows' + 'ms-dos' = 'Windows' + 'reactos' = 'Windows' + 'rhel' = 'RHEL Family' + 'centos' = 'RHEL Family' + 'rocky' = 'RHEL Family' + 'almalinux' = 'RHEL Family' + 'oraclelinux' = 'RHEL Family' + 'scientific' = 'RHEL Family' + 'eurolinux' = 'RHEL Family' + 'navylinux' = 'RHEL Family' + 'fedora' = 'Fedora Family' + 'qubes' = 'Fedora Family' + 'coreos' = 'Fedora Family' + 'opensuse' = 'SUSE Family' + 'sles' = 'SUSE Family' + 'geckolinux' = 'SUSE Family' + 'debian' = 'Debian Family' + 'ubuntu' = 'Ubuntu Family' + 'lubuntu' = 'Ubuntu Family' + 'kubuntu' = 'Ubuntu Family' + 'xubuntu' = 'Ubuntu Family' + 'linuxmint' = 'Ubuntu Family' + 'lmde' = 'Debian Family' + 'pop_os' = 'Ubuntu Family' + 'elementary' = 'Ubuntu Family' + 'zorin' = 'Ubuntu Family' + 'kali' = 'Security' + 'parrot' = 'Security' + 'backbox' = 'Security' + 'tails' = 'Security' + 'whonix' = 'Security' + 'blackarch' = 'Security' + 'raspios' = 'Debian Family' + 'armbian' = 'Debian Family' + 'devuan' = 'Debian Family' + 'mx' = 'Debian Family' + 'antix' = 'Debian Family' + 'deepin' = 'Debian Family' + 'pureos' = 'Debian Family' + 'grml' = 'Debian Family' + 'libreelec' = 'Media' + 'osmc' = 'Media' + 'arch' = 'Arch Family' + 'manjaro' = 'Arch Family' + 'endeavouros' = 'Arch Family' + 'garuda' = 'Arch Family' + 'artix' = 'Arch Family' + 'cachyos' = 'Arch Family' + 'arcolinux' = 'Arch Family' + 'gentoo' = 'Gentoo Family' + 'funtoo' = 'Gentoo Family' + 'calculate' = 'Gentoo Family' + 'void' = 'Independent Linux' + 'nixos' = 'Independent Linux' + 'alpine' = 'Independent Linux' + 'solus' = 'Independent Linux' + 'clearlinux' = 'Independent Linux' + 'flatcar' = 'Independent Linux' + 'slackware' = 'Slackware Family' + 'salix' = 'Slackware Family' + 'porteus' = 'Slackware Family' + 'pfsense' = 'Network & Firewall' + 'opnsense' = 'Network & Firewall' + 'ipfire' = 'Network & Firewall' + 'vyos' = 'Network & Firewall' + 'openwrt' = 'Network & Firewall' + 'ddwrt' = 'Network & Firewall' + 'proxmox-ve' = 'Virtualisation' + 'proxmox-bs' = 'Virtualisation' + 'proxmox-mg' = 'Virtualisation' + 'esxi' = 'Virtualisation' + 'vsphere' = 'Virtualisation' + 'xcp-ng' = 'Virtualisation' + 'xenserver' = 'Virtualisation' + 'harvester' = 'Virtualisation' + 'freebsd' = 'BSD Family' + 'openbsd' = 'BSD Family' + 'netbsd' = 'BSD Family' + 'dragonflybsd' = 'BSD Family' + 'truenas' = 'BSD Family' + 'ghostbsd' = 'BSD Family' + 'openindiana' = 'Solaris & illumos' + 'omnios' = 'Solaris & illumos' + 'smartos' = 'Solaris & illumos' + 'solaris' = 'Solaris & illumos' + 'haiku' = 'Exotic' + 'freedos' = 'Exotic' + 'reactos' = 'Exotic' + 'clonezilla' = 'Rescue & Tools' + 'systemrescue' = 'Rescue & Tools' + 'gparted' = 'Rescue & Tools' + 'memtest' = 'Rescue & Tools' + 'memtest86' = 'Rescue & Tools' + 'rescuezilla' = 'Rescue & Tools' + 'hirens' = 'Rescue & Tools' + 'winpe' = 'Rescue & Tools' + 'dban' = 'Rescue & Tools' + 'ubcd' = 'Rescue & Tools' + 'kaspersky-rescue' = 'Rescue & Tools' + 'bitdefender-rescue' = 'Rescue & Tools' + 'eset-rescue' = 'Rescue & Tools' +} + +function Get-Category { param([string]$slug) + # exact match first + if ($categoryMap.Contains($slug)) { return $categoryMap[$slug] } + # prefix match for variant slugs (e.g. opensuse-leap -> SUSE Family) + foreach ($key in $categoryMap.Keys) { + if ($slug -like "$key*") { return $categoryMap[$key] } + } + return 'Other' +} + +function Get-DisplayName { param([string]$slug, [string]$version, [string]$arch, [string]$filename) + $base = if ($displayNames.Contains($slug)) { $displayNames[$slug] } else { + # Title-case the slug as fallback + (Get-Culture).TextInfo.ToTitleCase($slug.Replace('-', ' ').Replace('_', ' ')) + } + $archLabel = if ($arch -ne 'noarch') { " [$arch]" } else { '' } + return "$base $version$archLabel" +} + +# ── Scan directory tree and build entries ───────────────────────────────────── +# Expected structure: $IsoRoot\\\\*.iso + +$entries = @() + +Get-ChildItem -Path $IsoRoot -Recurse -Filter "*.iso" | ForEach-Object { + $isoFile = $_ + + # Derive relative path from IsoRoot for the Ventoy menu path + $relativePath = $isoFile.FullName.Substring((Resolve-Path $IsoRoot).Path.Length).TrimStart('\') + + # Extract os/version/arch from directory structure + $parts = $relativePath -split '\\' + if ($parts.Count -ge 4) { + # Structure: os\version\arch\file.iso + $slug = $parts[0] + $version = $parts[1] + $arch = $parts[2] + } elseif ($parts.Count -ge 3) { + $slug = $parts[0] + $version = $parts[1] + $arch = 'noarch' + } else { + $slug = 'unknown' + $version = 'unknown' + $arch = 'noarch' + } + + $entries += [PSCustomObject]@{ + Slug = $slug + Version = $version + Arch = $arch + Category = Get-Category $slug + Label = Get-DisplayName $slug $version $arch $isoFile.Name + Path = '/' + $relativePath.Replace('\', '/') + File = $isoFile.Name + } +} + +# ── Build ventoy.json structure ─────────────────────────────────────────────── +# Group into categories, then sort categories and entries within them + +$menuItems = [ordered]@{} + +foreach ($entry in ($entries | Sort-Object Category, Slug, Version)) { + $cat = $entry.Category + if (-not $menuItems.Contains($cat)) { + $menuItems[$cat] = @() + } + $menuItems[$cat] += $entry +} + +# Preferred category order — anything not listed lands at the end alphabetically +$categoryOrder = @( + 'Windows', + 'RHEL Family', + 'Fedora Family', + 'SUSE Family', + 'Debian Family', + 'Ubuntu Family', + 'Arch Family', + 'Gentoo Family', + 'Slackware Family', + 'Independent Linux', + 'Security', + 'Network & Firewall', + 'Virtualisation', + 'BSD Family', + 'Solaris & illumos', + 'Media', + 'Exotic', + 'Rescue & Tools', + 'Other' +) + +$sortedCategories = @() +foreach ($cat in $categoryOrder) { + if ($menuItems.Contains($cat)) { $sortedCategories += $cat } +} +foreach ($cat in ($menuItems.Keys | Sort-Object)) { + if ($sortedCategories -notcontains $cat) { $sortedCategories += $cat } +} + +# Build the menu array ventoy expects +$menuArray = @() +foreach ($cat in $sortedCategories) { + $subItems = @() + foreach ($entry in $menuItems[$cat]) { + $subItems += [ordered]@{ + image = $entry.Path + menu_alias = $entry.Label + } + } + $menuArray += [ordered]@{ + name = $cat + image = $subItems + } +} + +$ventoyJson = [ordered]@{ + "menu_alias" = $menuArray +} + +# ── Write output ────────────────────────────────────────────────────────────── +$json = $ventoyJson | ConvertTo-Json -Depth 10 +$json | Set-Content -Path $OutFile -Encoding UTF8 + +Write-Host "Written: $OutFile" +Write-Host "" +Write-Host "Menu structure:" +foreach ($cat in $sortedCategories) { + Write-Host " [$cat]" -ForegroundColor Cyan + foreach ($entry in $menuItems[$cat]) { + Write-Host " $($entry.Label)" -ForegroundColor Gray + Write-Host " $($entry.Path)" -ForegroundColor DarkGray + } +} +Write-Host "" +Write-Host "Copy ventoy.json to the ventoy\ folder on your Ventoy USB drive." \ No newline at end of file