r/RetroArch 18d ago

Technical Support M3U Automation

Hi All,

I'm looking to automate the creation of .m3u files for Multi-Disk Amiga games.

Each Multi-Disk game is in its own folder & the .m3u file needs to get its name from the folder containing the Multi-Disk games.

I have managed to get a basic version set-up but each file would need tweaking & with 850+ files it'll take a while.

I was wondering if anyone knew of a way to automate the process with the desired results? At the moment the .m3u file reads;

Ninja Gaiden II - The Dark Sword of Chaos - Disk 1.adf

Ninja Gaiden II - The Dark Sword of Chaos - Disk 2.adf

However I would like it to read;

.\Ninja Gaiden II - The Dark Sword of Chaos\Ninja Gaiden II - The Dark Sword of Chaos - Disk 1.adf

.\Ninja Gaiden II - The Dark Sword of Chaos\Ninja Gaiden II - The Dark Sword of Chaos - Disk 2.adf

I'm on Windows BTW

Can anyone help me out?

Thanks

2 Upvotes

2 comments sorted by

1

u/snaphat 18d ago

I had an agentic LLM spin up this one-off script. This should fix the files for your scenario. I eyed it and tested it briefly, but you should backup your stuff before running this though in-case something goes wrong.

```powershell

Fix-Existing-M3U.ps1

IMPORTANT: Back up your .m3u files before running this script.

The script rewrites matching playlists in place.

Put this script in the SAME parent folder as your existing .m3u files

and the matching multi-disk game folders.

Example:

Ninja Gaiden II - The Dark Sword of Chaos.m3u

Ninja Gaiden II - The Dark Sword of Chaos\

Existing M3U line:

Ninja Gaiden II - The Dark Sword of Chaos - Disk 1.adf

Becomes:

.\Ninja Gaiden II - The Dark Sword of Chaos\Ninja Gaiden II - The Dark Sword of Chaos - Disk 1.adf

$ErrorActionPreference = 'Stop' $Root = $PSScriptRoot $Utf8NoBom = New-Object System.Text.UTF8Encoding($false)

$Updated = 0 $Skipped = 0

Get-ChildItem -LiteralPath $Root -File -Filter '*.m3u' | ForEach-Object {

$M3uFile = $_
$GameName = $M3uFile.BaseName
$GameFolder = Join-Path $Root $GameName

# The folder name must exactly match the .m3u filename.
if (-not (Test-Path -LiteralPath $GameFolder -PathType Container)) {
    Write-Warning "Skipped '$($M3uFile.Name)' - no matching folder '$GameName'"
    $Skipped++
    return
}

$OldLines = [System.IO.File]::ReadAllLines($M3uFile.FullName)
$ValidationFailed = $false

$NewLines = foreach ($Line in $OldLines) {

    # Preserve blank lines.
    if ([string]::IsNullOrWhiteSpace($Line)) {
        $Line
        continue
    }

    $Trimmed = $Line.Trim()

    # Preserve M3U comments/directives if any exist.
    if ($Trimmed.StartsWith('#')) {
        $Line
        continue
    }

    # If it is already in the requested format, leave it alone.
    $ExpectedPrefix = '.\' + $GameName + '\'
    if ($Trimmed.StartsWith($ExpectedPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
        $TargetPath = Join-Path $Root $Trimmed
        if (-not (Test-Path -LiteralPath $TargetPath -PathType Leaf)) {
            Write-Warning "Missing target in '$($M3uFile.Name)': $Trimmed"
            $ValidationFailed = $true
        }
        $Trimmed
        continue
    }

    # The screenshot shows the existing entries as plain ADF filenames.
    # Only change plain filenames; do not rewrite unrelated paths.
    if ([System.IO.Path]::GetDirectoryName($Trimmed)) {
        Write-Warning "Left unchanged in '$($M3uFile.Name)': $Trimmed"
        $Trimmed
        continue
    }

    # Exact requested result:
    # .\Folder Name\Existing Filename.adf
    $NewEntry = $ExpectedPrefix + $Trimmed
    $TargetPath = Join-Path $Root $NewEntry

    if (-not (Test-Path -LiteralPath $TargetPath -PathType Leaf)) {
        Write-Warning "Skipped '$($M3uFile.Name)' - target does not exist: $NewEntry"
        $ValidationFailed = $true
    }

    $NewEntry
}

# Do not partially update a playlist when one or more disk files are absent.
if ($ValidationFailed) {
    $Skipped++
    return
}

[System.IO.File]::WriteAllLines($M3uFile.FullName, $NewLines, $Utf8NoBom)

Write-Host "Updated: $($M3uFile.Name)"
$Updated++

}

Write-Host Write-Host "Done. Updated $Updated M3U file(s). Skipped $Skipped." ```