[Request] Automatic title scanning

Everything related to MakeMKV
Post Reply
mikee
Posts: 1
Joined: Mon Mar 04, 2019 7:07 am

[Request] Automatic title scanning

Post by mikee » Mon Mar 04, 2019 7:18 am

I've noticed that when MakeMKV starts up it will automatically scans the disc to show the list of titles.

Can the same be enabled when MakeMKV is already running and a new disc is inserted?
It's already detecting the new drive and presents the huge button, I'm asking for that button to press itself.

Not sure if this is a feature request or it's already possible.

Woodstock
Posts: 10865
Joined: Sun Jul 24, 2011 11:21 pm

Re: [Request] Automatic title scanning

Post by Woodstock » Mon Mar 04, 2019 1:47 pm

I've noticed that when MakeMKV starts up it will automatically scans the disc to show the list of titles.
It does? Not that I've seen.

Starting MakeMKV with a disk inserted, the disk gets scanned enough to display its information in the Info window, but I still have to click the Open button to get the directory read.

If no disk is inserted when it is started, inserting a disk takes me to the same screen, and I have to click the Open button to get the directory.

WoofGrrrr
Posts: 5
Joined: Tue Mar 31, 2020 4:01 pm
Location: Vermont, USA

Re: [Request] Automatic title scanning

Post by WoofGrrrr » Tue Mar 31, 2020 4:06 pm

One more vote for this. It would speed up ripping disks for me.

When I pop a disk in, I typically get involved with something else and fail to notice when the disk is loaded. The app just sits there, waiting for me to click the Open button.

At the very least, if there could be a reminder, like when initial loading is complete, it would help a bit.

eonmc2
Posts: 2
Joined: Tue Jan 12, 2021 7:33 pm

Re: [Request] Automatic title scanning

Post by eonmc2 » Fri Mar 20, 2026 6:55 pm

Any update on this? It would be great if MakeMKV could auto-scan and go straight to the title selection automatically.

In the meantime, I put together a PowerShell script as a workaround. It relies on forcing window focus and sending keystrokes, so it might be a bit clunky/unstable, but it gets the job done for now.

Code: Select all

# MakeMKV_Opener_Ejector.ps1
# Author: Gemini + Claude Sonnet 4.6 Thinking
# Version: 1.0.5
# Combined Auto-Open-Disc and Auto-Eject Monitor

param(
    [string]$DriveLetter = "D",
    [string]$MakeMKVPath = "C:\Program Files (x86)\MakeMKV\makemkv.exe",
    [string]$LogFile = "$env:USERPROFILE\MakeMKV_log.txt",
    [int]$PollSeconds = 3,
    [int]$MaxScanAttempts = 5,
    [int]$ScanConfirmTimeoutSeconds = 8
)

if ($DriveLetter -match '([A-Za-z])') { 
    $CleanDrive = $matches[1].ToUpper() 
} else { 
    $CleanDrive = "D" 
}
$root = "${CleanDrive}:\"
$hasRunForLabel = $null
$TempLog = "$env:TEMP\MakeMKV_master_shadow_$PID.txt"

Add-Type @"
using System;
using System.Runtime.InteropServices;
public class WinAPI {
    [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
    [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
    [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
    [DllImport("user32.dll")] public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
    [DllImport("kernel32.dll")] public static extern uint GetCurrentThreadId();
    [DllImport("user32.dll")] public static extern bool BlockInput(bool fBlockIt);
}
"@

function Force-Focus([IntPtr]$hWnd) {
    if ($hWnd -eq [IntPtr]::Zero) { return $false }
    [WinAPI]::ShowWindowAsync($hWnd, 9) | Out-Null
    [WinAPI]::SetForegroundWindow($hWnd) | Out-Null
    $remoteThreadId = [WinAPI]::GetWindowThreadProcessId($hWnd, [IntPtr]::Zero)
    $currentThreadId = [WinAPI]::GetCurrentThreadId()
    if ($remoteThreadId -ne $currentThreadId) {
        [WinAPI]::AttachThreadInput($currentThreadId, $remoteThreadId, $true) | Out-Null
        [WinAPI]::SetForegroundWindow($hWnd) | Out-Null
        [WinAPI]::AttachThreadInput($currentThreadId, $remoteThreadId, $false) | Out-Null
    }
    return $true
}

function Send-OpenDiscMacro($proc) {
    Add-Type -AssemblyName System.Windows.Forms
    if (-not (Force-Focus $proc.MainWindowHandle)) { return $false }
    try {
        [WinAPI]::BlockInput($true) | Out-Null
        Start-Sleep -Milliseconds 300
        [System.Windows.Forms.SendKeys]::SendWait("%(f)")
        Start-Sleep -Milliseconds 400
        [System.Windows.Forms.SendKeys]::SendWait("{DOWN}")
        Start-Sleep -Milliseconds 100
        [System.Windows.Forms.SendKeys]::SendWait("{DOWN}")
        Start-Sleep -Milliseconds 100
        [System.Windows.Forms.SendKeys]::SendWait("{RIGHT}")
        Start-Sleep -Milliseconds 300
        [System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
        return $true
    } finally {
        [WinAPI]::BlockInput($false) | Out-Null
    }
}

function Wait-ForReadySignal($baselineLines, $timeoutSeconds, $SourceLog, $ShadowLog) {
    $readyPattern = 'SDF id v'
    $deadline = (Get-Date).AddSeconds($timeoutSeconds)
    $bl = $baselineLines

    while ((Get-Date) -lt $deadline) {
        Copy-Item -LiteralPath $SourceLog -Destination $ShadowLog -Force -ErrorAction SilentlyContinue
        if (Test-Path $ShadowLog) {
            $allLines = @(Get-Content $ShadowLog -Encoding UTF8 -ErrorAction SilentlyContinue)
            if ($allLines.Count -lt $bl) { $bl = 0 }
            $newLines = $allLines | Select-Object -Skip $bl
            if ($newLines | Where-Object { $_ -match $readyPattern }) {
                return $true
            }
        }
        Start-Sleep -Milliseconds 500
    }
    return $false
}

function Wait-ForLogLine($baselineLines, $timeoutSeconds, $SourceLog, $ShadowLog) {
    $successPattern = 'Using direct disc access mode|Launching.*mmgplsrv64\.exe|Title #\d+ was added'
    $deadline = (Get-Date).AddSeconds($timeoutSeconds)
    $bl = $baselineLines
    
    while ((Get-Date) -lt $deadline) {
        if (Test-Path $SourceLog) {
            Copy-Item -LiteralPath $SourceLog -Destination $ShadowLog -Force -ErrorAction SilentlyContinue
            if (Test-Path $ShadowLog) {
                $allLines = @(Get-Content $ShadowLog -Encoding UTF8 -ErrorAction SilentlyContinue)
                
                if ($allLines.Count -lt $bl) {
                    $bl = 0
                }
                
                $newLines = $allLines | Select-Object -Skip $bl
                if ($newLines | Where-Object { $_ -match $successPattern }) {
                    return $true
                }
            }
        }
        Start-Sleep -Milliseconds 500
    }
    return $false
}

Write-Host "=== MakeMKV Opener & Ejector Monitor ===" -ForegroundColor Cyan
Write-Host "Watching drive $CleanDrive for new discs and rip completions..." -ForegroundColor Gray

$lastSize = 0
$seenLines = 0

if (Test-Path $LogFile) {
    Copy-Item -LiteralPath $LogFile -Destination $TempLog -Force -ErrorAction SilentlyContinue
    if (Test-Path $TempLog) {
        $lastSize = (Get-Item $TempLog).Length
        $seenLines = @(Get-Content $TempLog -Encoding UTF8 -ErrorAction SilentlyContinue).Count
    }
}

try {
    while ($true) {
        try {
            $di = New-Object System.IO.DriveInfo($root)
            
            # --- 1. SCAN LOGIC ---
            if ($di.IsReady) {
                $label = $di.VolumeLabel
                if (-not $label) { $label = "__NO_LABEL__" }

                if ($hasRunForLabel -ne $label) {
                    Write-Host "`n$(Get-Date -Format 'HH:mm:ss') -> New disc detected: $label" -ForegroundColor Green
                    $hasRunForLabel = $label

                    $proc = Get-Process makemkv -ErrorAction SilentlyContinue | 
                            Where-Object { $_.MainWindowHandle -ne 0 } | 
                            Select-Object -First 1

                    if (-not $proc) {
                        Write-Host "Launching MakeMKV..." -ForegroundColor DarkGray
                        Start-Process -FilePath $MakeMKVPath -WindowStyle Normal
                        $waited = 0
                        while ($waited -lt 15) {
                            Start-Sleep -Seconds 1
                            $waited++
                            $proc = Get-Process makemkv -ErrorAction SilentlyContinue | 
                                    Where-Object { $_.MainWindowHandle -ne 0 } | 
                                    Select-Object -First 1
                            if ($proc) { break }
                        }
                    }

                    if ($proc) {
                        $baselineLines = 0
                        if (Test-Path $LogFile) {
                            Copy-Item -LiteralPath $LogFile -Destination $TempLog -Force -ErrorAction SilentlyContinue
                            if (Test-Path $TempLog) {
                                $baselineLines = @(Get-Content $TempLog -Encoding UTF8 -ErrorAction SilentlyContinue).Count
                            }
                        }
                        
                        Write-Host "Waiting for MakeMKV ready signal (SDF id)..." -ForegroundColor DarkGray
                        if (-not (Wait-ForReadySignal $baselineLines 60 $LogFile $TempLog)) {
                            Write-Host "MakeMKV did not become ready in time." -ForegroundColor Red
                        } else {
                            Write-Host "MakeMKV ready. Sending Open Disc macro..." -ForegroundColor DarkGray
                            
                            $scanConfirmed = $false
                            for ($attempt = 1; $attempt -le $MaxScanAttempts; $attempt++) {
                                Write-Host "Sending scan command (attempt $attempt)..." -ForegroundColor DarkGray
                                Send-OpenDiscMacro $proc | Out-Null
                                
                                Write-Host "Waiting for scan confirmation in log (${ScanConfirmTimeoutSeconds}s)..." -ForegroundColor DarkGray
                                if (Wait-ForLogLine $baselineLines $ScanConfirmTimeoutSeconds $LogFile $TempLog) {
                                    Write-Host "Scan confirmed!" -ForegroundColor Green
                                    $scanConfirmed = $true
                                    break
                                } else {
                                    Write-Host "Not confirmed yet, retrying..." -ForegroundColor Yellow
                                }
                            }
                            
                            if (-not $scanConfirmed) {
                                Write-Host "Scan could not be confirmed after $MaxScanAttempts attempts." -ForegroundColor Red
                            }
                        }
                    } else {
                        Write-Host "MakeMKV did not start in time." -ForegroundColor Red
                    }

                    if (Test-Path $LogFile) {
                        Copy-Item -LiteralPath $LogFile -Destination $TempLog -Force -ErrorAction SilentlyContinue
                        if (Test-Path $TempLog) {
                            $lastSize = (Get-Item $TempLog).Length
                            $seenLines = @(Get-Content $TempLog -Encoding UTF8 -ErrorAction SilentlyContinue).Count
                        }
                    }
                    continue 
                }
            } else {
                if ($null -ne $hasRunForLabel) {
                    Write-Host "$(Get-Date -Format 'HH:mm:ss') -> Disc removed." -ForegroundColor DarkGray
                }
                $hasRunForLabel = $null
            }

            # --- 2. EJECT LOGIC ---
            if (Test-Path $LogFile) {
                Copy-Item -LiteralPath $LogFile -Destination $TempLog -Force -ErrorAction SilentlyContinue
                if (Test-Path $TempLog) {
                    $currentSize = (Get-Item $TempLog).Length
                    
                    if ($currentSize -gt $lastSize) {
                        $allLines = @(Get-Content $TempLog -Encoding UTF8 -ErrorAction SilentlyContinue)
                        
                        if ($allLines.Count -lt $seenLines) {
                            $seenLines = 0
                        }
                        
                        $newLines = $allLines | Select-Object -Skip $seenLines
                        $seenLines = $allLines.Count
                        $lastSize = $currentSize
                        
                        foreach ($line in $newLines) {
                            if ($line -match 'Copy complete\. (\d+) titles? saved') {
                                $titleCount = [int]$matches[1]
                                if ($titleCount -eq 0) { continue }
                                
                                Write-Host "`n>>> RIP COMPLETE! ($titleCount titles saved) <<<" -ForegroundColor Green
                                Write-Host "Closing MakeMKV popup..." -ForegroundColor Cyan
                                
                                $proc = Get-Process makemkv -ErrorAction SilentlyContinue | 
                                        Where-Object { $_.MainWindowHandle -ne 0 } | 
                                        Select-Object -First 1

                                if ($proc) {
                                    Add-Type -AssemblyName System.Windows.Forms
                                    $popupClosed = $false
                                    $attempt = 0
                                    
                                    while (-not $popupClosed -and $attempt -lt 3) {
                                        $attempt++
                                        Start-Sleep -Seconds 3 
                                        
                                        try {
                                            [WinAPI]::BlockInput($true) | Out-Null
                                            [WinAPI]::ShowWindowAsync($proc.MainWindowHandle, 9) | Out-Null
                                            [WinAPI]::SetForegroundWindow($proc.MainWindowHandle) | Out-Null
                                            Start-Sleep -Milliseconds 600
                                            
                                            [System.Windows.Forms.SendKeys]::SendWait(" ")
                                            Start-Sleep -Milliseconds 200
                                            [System.Windows.Forms.SendKeys]::SendWait("{ESC}")
                                            Start-Sleep -Milliseconds 200
                                            [System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
                                            
                                            Write-Host "Popup keystrokes sent (attempt $attempt)." -ForegroundColor Green
                                            $popupClosed = $true
                                        } finally {
                                            [WinAPI]::BlockInput($false) | Out-Null
                                        }
                                    }
                                } else {
                                    Write-Host "MakeMKV GUI process not found." -ForegroundColor Yellow
                                }

                                if ($titleCount -eq 1) {
                                    Write-Host "Single title saved. Ejecting drive..." -ForegroundColor Green
                                    $ejected = $false
                                    try {
                                        $wmp = New-Object -ComObject WMPlayer.OCX.7
                                        $cdroms = $wmp.cdromCollection
                                        for ($i = 0; $i -lt $cdroms.Count; $i++) {
                                            if ($cdroms.Item($i).DriveSpecifier -match "^$CleanDrive") {
                                                $cdroms.Item($i).Eject()
                                                $ejected = $true
                                                break
                                            }
                                        }
                                        if (-not $ejected) { throw "Not in WMP" }
                                    } catch {
                                        try {
                                            $shell = New-Object -ComObject Shell.Application
                                            $shell.Namespace(17).ParseName("$CleanDrive`:").InvokeVerb("Eject")
                                        } catch {
                                            if ($proc) {
                                                Write-Host "COM Eject failed, using Menu fallback..." -ForegroundColor Yellow
                                                try {
                                                    [WinAPI]::BlockInput($true) | Out-Null
                                                    [WinAPI]::ShowWindowAsync($proc.MainWindowHandle, 9) | Out-Null
                                                    [WinAPI]::SetForegroundWindow($proc.MainWindowHandle) | Out-Null
                                                    Start-Sleep -Milliseconds 300
                                                    [System.Windows.Forms.SendKeys]::SendWait("%(f)")
                                                    Start-Sleep -Milliseconds 200
                                                    [System.Windows.Forms.SendKeys]::SendWait("e")
                                                } finally {
                                                    [WinAPI]::BlockInput($false) | Out-Null
                                                }
                                            }
                                        }
                                    }
                                } else {
                                    Write-Host "Multiple titles ($titleCount) saved. TV Series Mode: Eject SKIPPED." -ForegroundColor Magenta
                                }

                                Write-Host "Playing completion sound..." -ForegroundColor Yellow
                                for ($i = 1; $i -le 4; $i++) {
                                    [System.Console]::Beep(750, 500)
                                    Start-Sleep -Milliseconds 500
                                }
                                
                                $wavPaths = @(
                                    "C:\Program Files (x86)\Elaborate Bytes\CloneDVD2\sounds\success.wav",
                                    "C:\Program Files (x86)\DVD Decrypter\Sounds\Success.wav",
                                    "C:\Program Files (x86)\ImgBurn\Sounds\Success.wav",
                                    "C:\Windows\Media\tada.wav"
                                )
                                foreach ($wav in $wavPaths) {
                                    if (Test-Path $wav) {
                                        try {
                                            $player = New-Object System.Media.SoundPlayer
                                            $player.SoundLocation = $wav
                                            $player.PlaySync()
                                            break
                                        } catch { }
                                    }
                                }
                                
                                Write-Host "Waiting for the next disc..." -ForegroundColor Cyan
                                break
                            }
                        }
                    }
                }
            }
        } catch { }
        
        Start-Sleep -Seconds $PollSeconds
    }
} finally {
    Remove-Item $TempLog -Force -ErrorAction SilentlyContinue
}

Post Reply