Fun with Powershell And TV on Blu Ray

Everything related to MakeMKV
Post Reply
ndjamena
Posts: 830
Joined: Mon Jan 07, 2013 12:23 am

Fun with Powershell And TV on Blu Ray

Post by ndjamena » Sat Oct 31, 2015 11:20 am

I just wasted the last few days writing a Powershell script for ripping TV Shows. It hunts down "Play All"s, titles without audio and Region A duplicates, sets both a minimum and maximum duration, renames files based on the source file and will even detect when a title is from a "Play All" and rename it as an enumerated part of the "Play All" and then rip them in file name order.

I've tried it on Heroes, Californication and Supernatural and it works a treat. It only works on Blu Rays though, DVDs don't have segment numbers or a lot of the other problems Blu Rays have.

I'm just playing around here so the things a mess, Min and Max duration are hard coded at the moment, they really ought to be set in the middle between the info getting and the actual ripping so you can modify things to your hearts content, but the script is here:

(You also need "MakeMKVFunctions.ps1" (in the next post), save them in the same directory as "MakeMKV.ps1" and "MakeMKVFunctions.ps1" and they should work. They work best in the Powershell ISE (YMMV))

"%windir%\system32\WindowsPowerShell\v1.0\PowerShell_ISE.exe"
%windir% is usually "C:\Windows\"

"MakeMKV.ps1"

Code: Select all

."$PSScriptRoot\MakeMKVFunctions.ps1"

if (-not ($MakeMKVCon = Get-MakeMKVCon)) { break }
$MKVPropEdit = Get-MKVPropEdit

cls

Skip-Lines 11

Write-Line Main Blue "Settings:"

    Write-Line Head Magenta "Source:"
        $DiscRecorder = Get-Disc
        if (-not $DiscRecorder) { Write-Line Main Blue "No Source."; break }
        $DiscRecorder.VolumePathNames | foreach {if ($_.length -eq 3 -and $_[1] -eq ':' -and $_[2] -eq '\') {$Drive=$_[0]}}
        $Disc = $DiscRecorder.LegacyDeviceNumber
        Write-Line Line Magenta "Drive [$Disc]: " 25 "$Drive`:\"
    Write-Line Head Magenta "Set."

    Write-Line Head Yellow "Destination:"
        $Dest = Get-Dest "PSMakeMKV.cfg" "Choose Destination Folder."
        Write-Line Line Yellow "Destination Folder:" 25 "$Dest"
    Write-Line Head Yellow "Set."

    Write-Line Head Cyan "Limitations:"
        if (($MinDuration=(Get-Duration "Minimum Durations.txt" "Minimum Duration" "Time")))
        { Write-Line Line Cyan "Minimum Duration:" 25 ([string]$MinDuration)}
        else {Write-Line Line Cyan "Minimum Duration:" 25 "None"}
        if (($MaxDuration=(Get-Duration "Maximum Durations.txt" "Maximum Duration" "Time")) -gt $MinDuration)
        { Write-Line Line Cyan "Maximum Duration:" 25 ([string]$MaxDuration)}
        else {Write-Line Line Cyan "Maximum Duration:" 25 "None"; $MaxDuration = 0}
        if (($MinChapters=(Get-Duration "Minimum Chapter Count.txt" "Minimum Chapter Count" "Chapters")))
        { Write-Line Line Cyan "Minimum Chapter Count:" 25 ([string]$MinChapters)}
        else {Write-Line Line Cyan "Minimum Chapter Count:" 25 "None"; $MinChapters = 0}
    Write-Line Head Cyan "Set"

    Write-Line Head DarkGreen "Profile:"
        $Profile = Get-Profile
        if ($Profile) { Write-Line Line DarkGreen "Profile:" 25 $Profile; $ProfileCommand="--profile=$Profile" }
        else { Write-Line Line DarkGreen "Profile:" 25 "Using Default Profile."; $ProfileCommand=$null }
    Write-Line Head DarkGreen "Set."

Write-Line Main Blue "All Set."

$DefaultProfile = [System.IO.Path]::GetDirectoryName($MakeMKVCon) + "\default.mmcp.xml"

if (-not ((Get-Item -Path "Running.tog" -ErrorAction SilentlyContinue))) {[void](New-Item -Path "Running.tog" -ItemType "file")}

while ($true)
{
    if (" " -eq " ") {
        if (-not ((Get-Item -Path "Running.tog" -ErrorAction SilentlyContinue))) { break }
        $DebugOn = $true
        while (-not (Get-WmiObject win32_logicaldisk -Filter "DeviceID=`"$Drive`:`" and Access>0")) { if ($DebugOn) { "`n"; Write-Line Main White "Please Insert Disc."; $DebugOn=$false }; Start-Sleep -Seconds 1 }
        $CurTitle = $CurStream = -1
        $DebugOn = $false
        $NewData = New-Object -TypeName XML
        [void]$NewData.AppendChild($NewData.CreateComment("$MakeMKVCon --noscan -r --messages=-stdout --profile=$DefaultProfile --minlength=0 info disc:$Disc"));
        #$NewData = [xml] "<?xml version=`"1.0`"?><!-- <!DOCTYPE DiscInfo SYSTEM `"MakeMKVDiscInfo.dtd`"> --><Disc />"
        $DiscData = $NewData.CreateElement("Disc")
        [void]$NewData.AppendChild($DiscData)
        $Dups = $NewData.CreateElement("Duplicates")
        [void]$DiscData.AppendChild($Dups)

        Write-Pillar

        Write-Line Main DarkRed "MakeMKV Scan:"
        Write-Line Head DarkCyan "Info:"
        $Title = 0
        &$MakeMKVCon "--noscan" "-r" "--messages=-stdout" ("--profile=$DefaultProfile") "--minlength=0" "info" ("disc:$Disc") | ForEach-Object -Process {
        
            $Type = $Line = $Temp = ""
            ([string] $_).Split(":", 2) | foreach {if ($Type) {$Line = $_} else {$Type = $_}}

            $EscapeOn = $InQuotes = $false
            $Params = @()
            switch -wildcard ($Line.ToCharArray())
            {
                '\' { if (-not $EscapeOn -and $InQuotes) { $EscapeOn = $true; continue } }
                ',' { if (-not $InQuotes) { $Params+=$Temp; $Temp=""; continue } }
                '"' { if (-not $EscapeOn) { $InQuotes = -not $InQuotes;  continue } }
                ? { $Temp+=$_; $EscapeOn = $false }
            }
            $Params+=$Temp
            switch -wildcard ($Type)
            {
                "DRV" { break }
                "TCOUNT" { break }
                "MSG"
                {
                    switch ([int]$Params[0])
                    {
                        $Message_DEBUG_ASSERT { Write-Line Line Red "Debug Assert:" ([int]-1) $Params[5] ([int]-1) "at" ([int]-1) $Params[6] }
                        $Message_OPENING_FILES { break }
                        $Message_TITLE_EQUAL {
                            $SourceNumber = ($Params[6].Split('('))[0]
                            $SourceNumber = $SourceNumber.Replace(".mpls", "").Replace(".m2ts", "")
                            $SourcePart = ""
                            if ($Params[6].Contains(".mpls("))
                            {
                                $SourcePart = ($Params[6].Split('('))[1].TrimEnd(')')
                            }
                            if ($Params[6].Contains(".mpls"))
                            {
                                $SourceEx = "mpls"
                            }
                            elseif ($Params[6].Contains(".m2ts"))
                            {
                                $SourceEx = "m2ts"
                            }
                            $Dup = $Dups.SelectSingleNode("Duplicate[@Source=""$SourceNumber"" and @Ext=""$SourceEx"" and @Part=""$SourcePart""]")
                            if (-not $Dup)
                            {
                                $Dup = Add-XML-Element $Dups "Duplicate"
                                Add-XML-Attribute $Dup "Source" $SourceNumber
                                Add-XML-Attribute $Dup "Part" $SourcePart
                                Add-XML-Attribute $Dup "Ext" $SourceEx
                            }
                            $SourceNumber = ($Params[5].Split('('))[0]
                            $SourceNumber = $SourceNumber.Replace(".mpls", "").Replace(".m2ts", "")
                            $SourcePart = ""
                            if ($Params[5].Contains(".mpls("))
                            {
                                $SourcePart = ($Params[5].Split('('))[1].TrimEnd(')')
                            }
                            if ($Params[5].Contains(".mpls"))
                            {
                                $SourceEx = "mpls"
                            }
                            elseif ($Params[5].Contains(".m2ts"))
                            {
                                $SourceEx = "m2ts"
                            }
                            $Copy = Get-XML-Element $Dup "Copy" "Source" $SourceNumber -Force
                            Add-XML-Attribute $Copy "Part" $SourcePart
                            Add-XML-Attribute $Copy "Ext" $SourceEx
                            break
                        }
                        $Message_TITLE_TOO_SMALL { break }
                        $Message_QUERY_OVERWRITE_FILE { break }
                        $Message_SAVE_FAILED { break }
                        $Message_SAVE_PART_SUCCESSFUL { break }
                        $Message_SAVE_SUCCESSFUL { break }
                        $Message_DISC_FAILED { break }
                        $Message_OPERATION_SUCCESSFUL { break }
                        $Message_COMPLETE_SUCCESSFUL { break }
                        $Message_COMPLETE_PART_SUCCESSFUL { break }
                        $Message_LOAD_HASH_TABLE { break }
                        $Message_DECRYPTOR_DETECTED { Write-Line Line DarkRed "Foreign Blu Ray Decryptor is Enabled"; }
                        $Message_AACS_NOT_PRESENT { Write-Line Line DarkRed "AACS Not Present" }
                        $Message_DIRECT_DISC_ACCESS { $DebugOn = $true; break }
                        $Message_DEBUG { if ($DebugOn) { Write-Line Line Red $Params[3] }; break }
                        $Message_DEBUG_LOG { Write-Line Line DarkCyan "Debug Log:" 22 $Params[5]; Write-Line Head DarkCyan "Read."; Write-Line Head DarkGreen "Disc Information:"; <#Write-Line Head Green "Titles:" ; #>break }
                        $Message_APP_START { Write-Line Line DarkCyan "Version:" 22 $Params[5]; break }
                        $Message_PATH_NOT_EXIST { Write-Host $Params[3]; break }
                        $Message_BUP_NOT_EQU_IFO { break }
                        $Message_TITLE_WITH_CELLS_ADDED { <#Write-Line Line Green "Title $Title" 12 "Added:" 20 $Params[5]; #>break }
                        $Message_TITLE_ADDED {
                            <#if ($Params[5].Contains(".mpls("))
                            {
                                $Params[5] = ($Params[5].Split('('))[0].Replace(".mpls","") + '-' + ($Params[5].Split('('))[1].TrimEnd(')').PadLeft(2, '0') + ".mpls"
                            }
                            Write-Line Line Green "Title $Title" 12 "Added:" 20 $Params[5]#>
                            $Title+=1
                            break
                        }
                        default { Write-Line Line DarkMagenta "Unknown Message (" $Params[0] "): " $Params[3]; Add-Content -Path "F:\Work\MakeMKV.log" -Value $_ }
                    }
                    break
                }
                "CINFO"
                {
                    $CurTitle = $CurStream = -1
                    $Params[0]=[convert]::ToInt32($Params[0], 10)
                    if ($Params[0] -eq 2) { Write-Line Line DarkGreen "Disc Name:" 22 $Params[2] }
                    if ($Params[0] -eq 32) { Write-Line Line DarkGreen "Volume Name:" 22 $Params[2] }
                    if ($Params[0] -lt $Attributes.Count) { Add-XML-Attribute $DiscData $Attributes[$Params[0]] $Params[2] }
                    break
                }
                "TINFO"
                {
                
                    $CurStream=-1
                    0..1 | foreach { $Params[$_]=[convert]::ToInt32($Params[$_], 10) }
                    if ($CurTitle -ne $Params[0]) { $CurTitleNode = Get-XML-Element $DiscData "Title" "Number" ($CurTitle=$Params[0]) -Force }
                    if ($Params[1] -eq 16)
                    {
                        $SourceNumber = ($Params[3].Split('('))[0]
                        $SourceNumber = $SourceNumber.Replace(".mpls", "").Replace(".m2ts", "")
                        $SourcePart = ""
                        if ($Params[3].Contains(".mpls("))
                        {
                            $SourcePart = ($Params[3].Split('('))[1].TrimEnd(')')
                        }
                        if ($Params[3].Contains(".mpls"))
                        {
                            $SourceEx = "mpls"
                        }
                        elseif ($Params[3].Contains(".m2ts"))
                        {
                            $SourceEx = "m2ts"
                        }
                        $CurTitleName = Add-XML-Element $CurTitleNode "Names"
                        $Copy = Get-XML-Element $CurTitleName "Name" "Source" $SourceNumber -Force
                        Add-XML-Attribute $Copy "Part" $SourcePart
                        Add-XML-Attribute $Copy "Ext" $SourceEx
                    }
                    if ($Params[1] -lt $Attributes.Count) { Add-XML-Attribute $CurTitleNode $Attributes[$Params[1]] $Params[3] }
                    if ($Params[1] -eq 26)
                    {
                        $Params[3]=$Params[3].Replace('(', '').Replace(')', '')
                        $CurTitleNode.SegmentsMap=$Params[3]
                        $Index=0
                        $Params[3].Split(',') | foreach {
                            if ($_ -ne "...") {
                                $Index++
                                [void] (Get-XML-Element (Get-XML-Element $DiscData "Segment" "Number" $_ -Force) "Title" "Number" $Params[0] -Force)
                                Add-XML-Attribute (Add-XML-Attribute (Add-XML-Element $CurTitleNode "Segment") "Number" $_ 2) "Order" $Index
                            }
                        }
                    }
                    break
                }
                "SINFO"
                {
                    0..2 | foreach { $Params[$_]=[convert]::ToInt32($Params[$_], 10) }                
                    if ($CurTitle -ne $Params[0]) { $CurTitleNode = Get-XML-Element $DiscData "Title" "Number" ($CurTitle=$Params[0]) -Force; $CurStream=-1 }
                    if ($CurStream -ne $Params[1]) { $CurStreamNode = Get-XML-Element $CurTitleNode "Stream" "Number" ($CurStream=$Params[1]) -Force }
                    if ($Params[2] -lt $Attributes.Count) { Add-XML-Attribute $CurStreamNode $Attributes[$Params[2]] $Params[4] }
                    break
                }
                default { "Unknown Output: " + $Type; break }
            }
        }
        #$NewData.Save("D:\" + $DiscData.Name + ".xml")
    }
    else
    {
        $NewData = [xml] (Get-Content $DiscName); $DiscData = $NewData.SelectSingleNode("Disc")
    }
    Write-Line Line DarkGreen "Number of Titles:" 22 ([string]$Title)
    Write-Line Line DarkGreen "Duplicated Titles:" 22 ([string]$Dups.Duplicate.count)
    Write-Line Head DarkGreen "Done."
    Write-Line Main DarkRed "Scan Complete."

    Write-Pillar

    Write-Line Main Gray "Title Decimation."

    Write-Line Head Green "Gathering Info On Titles."

    $DiscData.Title | foreach {
        Enum-Streams $_ "Video"
        Enum-Streams $_ "Audio"
        Enum-Streams $_ "Subtitles"
        Set-Times $_
    }

    
if ($DiscData.Type -eq "Blu-ray disc")
{
    $Dups.Duplicate | foreach {
        $Dup = $_
        $DiscData.Title | Where-Object {$Dup.Source -eq $_.Names.Name.Source -and $Dup.Part -eq $_.Names.Name.Part -and $Dup.Ext -eq $_.Names.Name.Ext} | foreach {
            $TitleNames=$_.Names
            $Dup.Copy | foreach {
                $TitleName=Add-XML-Element $TitleNames "Name"
                Add-XML-Attribute $TitleName "Source" $_.Source
                Add-XML-Attribute $TitleName "Part" $_.Part
                Add-XML-Attribute $TitleName "Ext" $_.Ext
            }
        }
    }
    $DiscData.SelectNodes("Title/Names/Name[@Part=1]") | foreach {
        $TitleName=$_.Source
        $DiscData.SelectNodes("Title/Names/Name[@Source=$TitleName]") | foreach {
            if ($_.Part)
            {
                $_.Part = [string]([convert]::ToInt32($_.Part) + 1)
            }
            else
            {
                $_.Part = "1"
            }
        }
    }
    $DiscData.SelectNodes("Title/Names/Name[@Part=1]") | foreach {
        $TitleName=$_.Source
        $TitleDuration=0
        $TitleSegmentsCount=0
        $TitleSegmentsMap=""
        $DiscData.SelectNodes("Title/Names/Name[@Source=$TitleName]") | Sort-Object Part | foreach {
            $TitleDuration+=[convert]::ToInt32($_.ParentNode.ParentNode.Duration.InSeconds)
            if ($TitleSegmentsMap)
            {
                $TitleSegmentsMap+=","
            }
            $TitleSegmentsMap+=$_.ParentNode.ParentNode.SegmentsMap
            $TitleSegmentsCount+=[convert]::ToInt32($_.ParentNode.ParentNode.SegmentsCount)
        }
        $TitleNumber=[convert]::ToInt32($DiscData.SelectSingleNode("Title[last()]").Number) + 1
        $NewTitle=Get-XML-Element $DiscData "Title" "Number" $TitleNumber -force
        Add-XML-Attribute $NewTitle "SourceFileName" ($TitleName + ".mpls")
        Add-XML-Attribute $NewTitle "SegmentsMap" $TitleSegmentsMap.Trim(',')
        Add-XML-Attribute $NewTitle "SegmentsCount" $TitleSegmentsCount
        Add-XML-Attribute $NewTitle "Dummy" "True"
        Add-XML-Attribute $NewTitle "Duration" (Convert-Timecode-To-Base-Duration $TitleDuration -AsSeconds -AsTimeCode -FromSeconds)
        Set-Times $NewTitle
        $a = Add-XML-Element (Add-XML-Element $NewTitle "Names") "Name"
        Add-XML-Attribute $a "Ext" $_.Ext
        Add-XML-Attribute $a "Source" $_.Source
        Add-XML-Attribute $a "Part" $_.Part
        $Index=0
        $TitleSegmentsMap.Split(',') | foreach {
            if ($_ -ne "...") {
                $Index++
                [void] (Get-XML-Element (Get-XML-Element $DiscData "Segment" "Number" $_ -Force) "Title" "Number" $TitleNumber -Force)
                Add-XML-Attribute (Add-XML-Attribute (Add-XML-Element $NewTitle "Segment") "Number" $_ 2) "Order" $Index
            }
        }
    }
    $DiscData.Title | foreach {
        $TitleName=""
        $TitleExt=""
        $TitleNameCount=0
        $_.SelectNodes("Names/Name") | Sort-Object Source | foreach {
            $TitleNameCount+=1
            if (-not $_.Part)
            {
                if ($TitleName) { $TitleName+="-" }
                $TitleName+=$_.Source
            }
            if ($_.Ext) {$TitleExt=$_.Ext}
        }
        if ($TitleNameCount -eq 1)
        {
             $TitleName+=("." + $TitleExt)
        }
        $_.SourceFileName = $TitleName
    }
    $DeleteList = Add-XML-Element $DiscData "DeleteList"

    $DiscData.Title | sort-object @{Expression={([object[]]$_.Names.Name)[0].Ext};Descending=$true}, SourceFileName | foreach {
        $ThisTitle = $_
        $TitleName = ""
        $ThisTitle.Names.Name | sort-object Source | foreach {
            if ($TitleName) { $TitleName+=", " }
            $TitleName+=$_.Source
            if ($_.Part)
            {
                $TitleName+="-" + $_.Part.PadLeft(2,'0')
            }
            $TitleName+="." + $_.Ext
        }
        if ($ThisTitle.Dummy)
        {
            $TitleName+=" (Dummy)"
        }
        $TitleName += " - " + $ThisTitle.Duration.TimeCode + " (" + $ThisTitle.Duration.InSeconds + " secs)"
        if ($ThisTitle.ChapterCount)
        {
            $TitleName += " - " + $ThisTitle.ChapterCount + " Chapters."
        }
        Write-Line Line Green "Title " $_.Number 13 "Added:" 22 $ThisTitle.SegmentsMap (-3) "-" (-3) $TitleName
    }
    Write-Line Head Green "Done."

    Write-Line Head White "Gathering Info On Segments."
    $DiscData.SelectNodes("Title") | foreach {
        Enum-Unique-Segments $_
    }
    do
    {
        $change2=$false
        $Pseudo = Add-XML-Element $DiscData "PseudoTitles"
        do
        {
            $Change=$false
            $DiscData.Title | foreach {
                $Count=0
                $Total=_I ($Title=$_).Duration.InSeconds
                $Segment=$null
                $Occurrences=0
                Get-Title-Segments $_ -Order | foreach {
                    if ($_.Duration -eq $null) { if ($Segment -ne $_) {$Count++ ; $Segment=$_; $Occurrences=1 } else { $Occurrences++ } }
                    else { $Total-=$_.Duration }
                }
                if ($Count -eq 1)
                {
                    Add-XML-Attribute $Segment "Duration" ((_I $Total) / $Occurrences)
                    $Change2=$Change=$true
                }            
            }
        }
        while ($Change)
        do
        {
            $Change=$false
            $DiscData.Title | foreach {
                $ThisTitle = $_
                $SegmentsMap = ',' + $ThisTitle.SegmentsMap + ','
                $Duration = 0
                $PTitle=$null
                $SegmentsMap.Split(',') | where {$_ -ne "" -and $_ -ne "..."} | foreach {
                    $Segment = $DiscData.SelectSingleNode("Segment[@Number=$_]")
                    if ($Segment.Duration) { $Duration+=$Segment.Duration.InSeconds }
                    else
                    {
                        if (-not $PTitle) { $PTitle = Get-XML-Element $Pseudo "Title" "Number" $ThisTitle.Number -Force }
                        if (-not ($PSegment = $PTitle.SelectSingleNode("Segment[@Number=$_]")))
                        { $PSegment = Get-XML-Element $PTitle "Segment" "Number" $_ -Force; Add-XML-Attribute $PSegment "Occurrences" 1 }
                        else { $PSegment.Occurrences = [string](1 + $PSegment.Occurrences) }
                    }
                }
                if ($PTitle)
                {
                    Add-XML-Attribute $PTitle "Duration" (([int]$ThisTitle.Duration.InSeconds) - $Duration)
                }
            }
        }
        while ($Change)
        if ($Pseudo.Title)
        {
            $Pseudo.Title | foreach {
                if ($_.Segment.count -ne 2)
                {
                    $ThisTitle = $_
                    $TitleNumber = $_.Number
                    $ThisTitle.Segment | foreach {
                        $ThisSegment = $_
                        $Number=$_.Number
                        if (-not $DiscData.SelectSingleNode("Segment[@Number!=$Number]").Duration)
                        {
                            $ThisTitle.Segment | foreach {
                                if ($_.Number -ne $Number)
                                {
                                        $Found = $Pseudo.SelectNodes("Title[Segment[@Number=$Number] and (@Number!=$TitleNumber) and Segment[2] and not(Segment[3])]")
                                    if ($Found.Count -ge ($ThisTitle.Segment.Count - 1))
                                    {
                                        $Seg = @()
                                        $Tit = @()
                                        $Found | foreach {
                                            $Seg+=$_.SelectSingleNode("Segment[@Number!=$Number]").Number
                                            $Tit+=$_.Number
                                        }
                                        $Last = -1
                                        $Seg = ( $Seg | Sort-Object | foreach { if ($_ -ne $Last) { $_ }; $Last = $_ } )
                                        if ($Seg.Count -eq ($ThisTitle.Segment.Count - 1))
                                        {
                                            $Last = -1
                                            $Tit2 = ( $Tit | Sort-Object | foreach { if ($_ -ne $Last) { $_ }; $Last = $_ } )
                                            $Last = 0
                                            $Tit2 | foreach {
                                                $Last+=[convert]::ToInt64($Pseudo.SelectSingleNode("Title[@Number=$_]").Duration)
                                            }
                                            Add-XML-Attribute ($DiscData.SelectSingleNode("Segment[@Number=$Number]")) "Duration" (([convert]::ToDouble($ThisTitle.Duration) - $Last)) 0
                                            $Change2=$true
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        [void] $DiscData.RemoveChild($Pseudo)
        $Pseudo = $null
        $DiscData.Segment | foreach {
            $ThisSegment = $_
            if ($ThisSegment.Duration -and [convert]::ToDouble($ThisSegment.Duration) -lt 3)
            {
                $Number = $ThisSegment.Number
                Get-Segment-Titles ([convert]::ToInt32($Number)) | foreach {
                    $ThisTitle = $_
                    $ThisTitle.SelectNodes("Segment[@Number=$Number]") | foreach { $ThisTitle.SegmentsCount = [string]([convert]::ToInt64($ThisTitle.SegmentsCount) - 1); [void]$ThisTitle.RemoveChild($_) }
                    $ThisTitle.SegmentsMap = ([string]$ThisTitle.SegmentsMap + ",").Replace(("," + $Number + ","), ",").TrimEnd(",")
                }
                [void]$DiscData.RemoveChild($ThisSegment)
                Write-Line Line Red "Removing: " -Cyan "Segment" ([int]-(6-$Number.Length)) $Number -White 30 "Reason: Too Insignificant To Factor In."
            }
        }
        $DiscData.Title | foreach {
            if ([convert]::ToInt64($_.SegmentsCount) -eq 0)
            {
                Write-Line Line Red "Removing: " -Green "Title" ([int]-(8-$_.Number.Length)) $_.Number -White 30 "Reason: No More Segments Left."
                [void]$DiscData.RemoveChild($_)
            }
        }
    }
    while ($Change2)
    $DiscData.Segment | Sort-Object @{ Expression={([int]$_.Number)} } | foreach {
        $ThisSegment = $_
        if ($ThisSegment.Duration)
        {
            Write-Line Line White "Segment:" ([int](0 - (6 - ([string]$ThisSegment.Number).Length))) -Yellow ([string]$ThisSegment.Number) -White 16 "Has a duration of " -Yellow (Convert-Timecode-To-Base-Duration $ThisSegment.Duration -AsTimecode -Fromseconds -AsSeconds) -White " (" $ThisSegment.Duration " Secs)."
        }
        else
        {
            Write-Line Line White "Segment:" ([int](0 - (6 - ([string]$ThisSegment.Number).Length))) ([string]$ThisSegment.Number) 16 "Has an Unknown duration."
        }
    }
    Write-Line Head White "Done."

    Write-Line Head Black "Processing Duplicate Titles."

    $DiscData.Title | foreach {
        $ThisTitle=$_
        $ThisTitleNumber=_I $ThisTitle.Number
        $ThisTitleChapterCount=_I $ThisTitle.ChapterCount
        $ThisTitleSegmentMap=[string]$ThisTitle.SegmentsMap
        $DiscData.Title | foreach {
            $NewTitle=$_
            if ($ThisTitleNumber -ne ($NewTitleNumber=_I $NewTitle.Number))
            {
                $NewTitleChapterCount=_I $NewTitle.ChapterCount
                if ($ThisTitleSegmentMap -eq ($NewTitleSegmentMap=[string]$NewTitle.SegmentsMap))
                {
                    if ($ThisTitleChapterCount -ne $NewTitleChapterCount)
                    {
                        if ($ThisTitleChapterCount -eq 0)
                        {
                            Get-Title-Segments $ThisTitle | foreach { Add-Delete-Request $ThisTitleNumber $_.Number "Duplicate With No Chapters" $NewTitleNumber 5 }
                        }
                    }
                    if ((($ThisTitle.SelectNodes("Stream[@Type=('Audio' or 'Subtitles') and @LangCode='jpn']")).Count) -and (-not ($NewTitle.SelectNodes("Stream[@Type=('Audio' or 'Subtitles') and @LangCode='jpn']")).Count) -and ($NewTitle.UniqueAudio -ge $ThisTitle.UniqueAudio) -and ($NewTitle.UniqueSubtitle -ge $ThisTitle.UniqueSubtitle))
                    {
                        Get-Title-Segments $ThisTitle | foreach { Add-Delete-Request $ThisTitleNumber $_.Number "Duplicate Region A Version" $NewTitleNumber 5 }
                    }
                }
            }
        }
    }
    Write-Line Head Black "Processing Contained Titles."
    $DiscData.Title | foreach {
        $ThisTitle=$_
        $ThisTitleNumber=_I $ThisTitle.Number
        $ThisTitleChapterCount=_I $ThisTitle.ChapterCount
        $ThisTitleSegmentMap=[string]$ThisTitle.SegmentsMap
        $ThisTitleSegments=(Get-Title-Segments $_)
        $ThisTitleSegmentCount=_I $ThisTitle.SegmentsCount
        $ContainedSegments = 0
        $Reason = 0
        $ThisTitleSegments | foreach {
            $ThisSegment = $_
            $ContainedSegment=0
            (Get-Segment-Titles $_ $ThisTitleNumber) | foreach {
                $NewTitle=$_
                $NewTitleNumber=_I $_.Number
                $Contained=0
                if ($NewTitle.SegmentsMap -ne $ThisTitle.SegmentsMap)
                {
                    $NewTitleChapterCount=_I $NewTitle.ChapterCount
                    $NewTitleSegmentCount=_I $NewTitle.SegmentsCount
                    $NewTitleSegments = Get-Title-Segments $NewTitle

                    if ($NewTitle.SegmentsMap.Contains($ThisTitle.SegmentsMap))
                    {
                        if ($ThisTitleSegmentCount -eq 1)
                        {
                            if (($NewTitleChapterCount -ne 0) -and ($ThisTitleChapterCount -eq 0))
                            {
                                Add-Delete-Request $ThisTitleNumber $ThisSegment.Number "Suspected Component Of Larger Title" $NewTitleNumber 5
                            }
                        }
                        else
                        {
                            if (($NewTitleChapterCount -ne 0) -and ($ThisTitleChapterCount -eq 0))
                            {
                                Add-Delete-Request $ThisTitleNumber $ThisSegment.Number "Suspected Component Of Larger Title" $NewTitleNumber 5
                            }
                        }
                    }
                    elseif ($ThisTitle.SegmentsMap.Contains($NewTitle.SegmentsMap))
                    {
                        if (($NewTitleChapterCount -ne 0) -and ($ThisTitleChapterCount -eq 0))
                        {
                                Add-Delete-Request $ThisTitleNumber $ThisSegment.Number "Suspected Amalgamation Of Better Titles" $NewTitleNumber 5 -Rename
                        }
                        else
                        {
                                Add-Delete-Request $ThisTitleNumber $ThisSegment.Number "Suspected Play All" $NewTitleNumber 5 -Rename
                        }
                    }
                    else
                    {
                        $Count = Compare-Segments $NewTitle.SegmentsMap $ThisTitle.SegmentsMap
                        if ($Count -eq $ThisTitle.SegmentsCount)
                        {
                                Add-Delete-Request $ThisTitleNumber $ThisSegment.Number "Suspected Expanded Version" $NewTitleNumber 0
                        }
                        elseif ($Count -eq $NewTitle.SegmentsCount)
                        {
                                Add-Delete-Request $ThisTitleNumber $ThisSegment.Number "Suspected Shortened Version" $NewTitleNumber 0
                        }
                    }
                }
            }
        }
    }
    Write-Line Head White "Removing Titles."
    $Nuke_Used=$true
    $DiscData.Title | foreach {
        if ($Nuke_Used)
        {
            if ($Nuke_Colour -eq "-Yellow")
            {
                $Nuke_Colour = "-White"
            }
            else
            {
                $Nuke_Colour = "-Yellow"
            }
            $Nuke_Used=$false
        }
        $NewTitle = $_
        if ($_.Dummy)
        {
            Nuke-Title $Newtitle "Temporary Title"
        }
        else
        {
            if (([int]$NewTitle.Duration.InSeconds) -lt $MinDuration)
            {
                Nuke-Title $Newtitle "Shorter Than Minimum Duration"
            }
            elseif ($MaxDuration -and (([double]$NewTitle.Duration.InSeconds) -gt $MaxDuration))
            {
                Nuke-Title $Newtitle "Longer Than Maximum Duration"
            }
            if (([int]$NewTitle.ChapterCount) -lt $MinChapters)
            {
                Nuke-Title $Newtitle "Not Enough Chapters"
            }
            if (-not $NewTitle.UniqueAudio)
            {
                Nuke-Title $Newtitle "No Audio Tracks"
            }
            if ($NewTitle.VideoQuality -lt 4)
            {
                Nuke-Title $Newtitle "Not HD Resolution"
            }
        }
    }
    $DiscData.Title | foreach {
        if ($Nuke_Used)
        {
            if ($Nuke_Colour -eq "-Yellow")
            {
                $Nuke_Colour = "-White"
            }
            else
            {
                $Nuke_Colour = "-Yellow"
            }
            $Nuke_Used=$false
        }
        $NewTitle = $_
        if ($ThisTitle=Get-XML-Element $DeleteList "Title" "Number" $NewTitle.Number)
        {
            $ThisSegment = ([Object[]]$ThisTitle.Segment)[0]
            $c=@()
            $ThisSegment.DeleteRequest | foreach {
                if (-not  $c.contains($_.Reason))
                {
                    $c += $_.Reason
                    $ThisDeleteRequest = $_
                    $b=$true
                    $ThisTitle.Segment | foreach {
                        if (-not $_.SelectSingleNode(("DeleteRequest[@Reason=""" + $ThisDeleteRequest.Reason + """]"))) { $b=$false }
                    }
                    if ($b) {
                        $a = @()
                        Get-Title-Segments $NewTitle -Order -Unique | foreach { $ThisTitle.SelectNodes(("Segment[@Number=" + $_.Number + "]")) } | foreach {
                            $_.SelectNodes("DeleteRequest[@Reason=""" + $ThisDeleteRequest.Reason + """]") | foreach { if (-not (([object[]] $a).Contains($_.Cause))) {$a+=$_.Cause} }
                        }
                        $b=""
                        $a | foreach { if (-not ([object[]]$DiscData.Title)[$_].Delete) {([string]$b)+=$_ + ","}}
                        if ($b -and (-not $NewTitle.Delete))
                        {
                            if (0 -ne $ThisDeleteRequest.Level)
                            {
                                [void] (Nuke-Title $Newtitle $ThisDeleteRequest.Reason ("[" + "$b".TrimEnd(',') + "]"))
                            }
                            else
                            {
                                Write-Line Line Green "Hint: Title" ([int]-1) (([string]$NewTitle.Number).PadLeft(([string]([object[]]$DiscData.Title).Count).Length)) " is a " $ThisDeleteRequest.Reason
                            }
                        }
                        if ($ThisDeleteRequest.Rename)
                        {
                            $d=0
                            $ThisDelete
                            Get-Title-Segments $NewTitle -Order -Unique | foreach {
                                Get-Segment-Titles $_ -Order | foreach {
                                    $SegmentTitle=$_
                                    if (($_.SegmentsCount -lt $NewTitle.SegmentsCount) -and (',' + $NewTitle.SegmentsMap + ',').Contains((',' + $_.SegmentsMap + ',')))
                                    {
                                        $e = ($NewTitle.SegmentsMap + ',')
                                        $f = 0
                                        $g = Get-XML-Element $_ "Rename" "Source" $NewTitle.SourceFileName -force
                                        Add-XML-Attribute $g "SourceList" ""
                                        Add-XML-Attribute $g "SourceCount" "0"
                                        Add-XML-Attribute $g "SourceDuration" $NewTitle.Duration.InSeconds
                                        Add-XML-Attribute $g "SourceNumber" $NewTitle.Number
                                        while ($e)
                                        {
                                            if ($e.StartsWith(($_.SegmentsMap + ',')))
                                            {
                                                $h = 0
                                                $_.SegmentsMap.Split(',') | foreach { $g.SourceList+=((",{0:" + ("0" * $NewTitle.SegmentsCount.ToString().Length) + "}") -f $f); $g.SourceCount=[string](1 + $g.SourceCount); $f++ }
                                                $e = $e.Remove(0, $_.SegmentsMap.Length + 1)
                                            }
                                            else
                                            {
                                                $e = $e.Remove(0, $e.Indexof(',') + 1)
                                                $f++
                                            }
                                        }
                                        $g.SourceList = $g.SourceList.Trim(',')
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    $a = ($DiscData.SelectNodes("Title[not(@Delete)]"))
    Write-Line Head White "Number Of Titles Left To Rip:" ([int]-1) ([string]([object[]]$a).Count)
    Write-Line Head DarkCyan -Cyan "Processing Playlists."
    $PlayLists = @()
    $DiscData.Title | foreach {
        $ThisTitle = $_
        if ($ThisTitle.Rename -and (-not $ThisTitle.Delete))
        {
            $a = ([Object[]]($_.Rename | Sort-Object {[double]$_.SourceDuration} -Descending))[0]
            $b = 0
            if ($PlayLists.count)
            {
                for ($b = 0; $b -lt ([Object[]]$PlayLists).count; $b++)
                {
                    if ($PlayLists[$b] -eq $a.SourceNumber)
                    {
                        break
                    }
                }
            }
            if ($b -eq ([Object[]]$PlayLists).count)
            {
                $PlayLists+=$a.SourceNumber
                Write-Line Line DarkCyan -Cyan "Title: " $a.SourceNumber "(" $a.Source ") is a playlist."
            }
        }
    }
    Write-Line Head DarkCyan -Cyan "Number of Playlists Found:" ([int]-1) ([string]([Object[]]$PlayLists).count)
    do
    {
        $Change = $false
        $DiscData.Title | foreach {
            $ThisTitle=$_
            if (-not $_.Rename)
            {
                $CanBe=""
                if (([object[]]$ThisTitle.Names.Name)[0].Ext -eq "mpls")
                {
                    $CanBeList=@()
                    $CanBePlus=@()
                    $CanBeMinus=@()
                    $_.Names.Name | foreach {
                        $ThisName = $_
                        $ThisNumber = [convert]::ToInt32($_.Source)

                        $FindName = ([string]($ThisNumber - 1)).PadLeft(5,'0')
                        $FoundTitle = $DiscData.SelectNodes("Title[(Names/Name/@Source=""$FindName"") and Rename]")
                        if (([Object[]]$FoundTitle).Count -gt 1)
                        {
                            $FoundTitle = ($FoundTitle.Names.Name | Where-Object { $_.Source -eq $FindName } | Sort-Object { [int]$_.Part })[0].ParentNode.ParentNode
                        }
                        if (([Object[]]$FoundTitle).Count -eq 1)
                        {
                            $a = ([object[]]($FoundTitle.Rename | Sort-Object {[int]$_.SourceDuration} -Descending))[0]
                            $CanBeList+= (([int]$a.SourceList) + 1)
                            $CanBePlus+=$a
                        }

                        $FindName = ([string]($ThisNumber + 1)).PadLeft(5,'0')
                        $FoundTitle = $DiscData.SelectNodes("Title[(Names/Name/@Source=""$FindName"") and Rename]")
                        if (([Object[]]$FoundTitle).Count -gt 1)
                        {
                            $FoundTitle = ($FoundTitle.Names.Name | Where-Object { $_.Source -eq $FindName } | Sort-Object { [int]$_.Part })[0].ParentNode.ParentNode
                        }
                        if (([Object[]]$FoundTitle).Count -eq 1)
                        {
                            $a = ([object[]]($FoundTitle.Rename | Sort-Object {[int]$_.SourceDuration} -Descending))[0]
                            $CanBeList+= (([int]$a.SourceList) - 1)
                            $CanBeMinus+=$a
                        }
                    }
                    $CanBe = ($CanBeList | Sort-Object -Unique)
                    if (([object[]]$CanBe).Count -eq 1)
                    {
                        $CanBe = ([object[]]$CanBe)[0]
                        if ($CanBeMinus)
                        {
                            $g = $CanBeMinus[0]
                        }
                        else
                        {
                            $g = $CanBePlus[0]
                        }
                        [void] $ThisTitle.AppendChild($g.CloneNode($true))
                        $ThisTitle.Rename.SourceList = [string]$CanBe
                        $Change=$True
                    }
                }
            }
        }
    }
    while ($Change)
    $DiscData.Title | foreach {
        $ThisTitle = $_
        if ($_.Rename)
        {
            $a = ([Object[]]($_.Rename | Sort-Object {[int]$_.SourceDuration} -Descending))[0]
            if (([Object[]]$PlayLists).count -lt 2)
            {
                if ($_.SourceFileName)
                {
                    Add-XML-Attribute $_ "OutputName" (([string](([int]$a.SourceList) + 1)).PadLeft(2,'0') + "-" + ([string]$_.SourceFileName).Replace(".m2ts", "").Replace(".mpls", "") + ".mkv")
                }
                else
                {
                    Add-XML-Attribute $_ "OutputName" (([string](([int]$a.SourceList) + 1)).PadLeft(2,'0') + "-" + ([string]$a.Source).Replace(".m2ts", "").Replace(".mpls", "") + ".mkv")
                }
            }
            else
            {
                if ($_.SourceFileName)
                {
                    Add-XML-Attribute $_ "OutputName" (([string]$_.Source).Replace(".m2ts", "").Replace(".mpls", "") + "-" + ([string](([int]$a.SourceList) + 1)).PadLeft(2,'0') + "-" + ([string]$_.SourceFileName).Replace(".m2ts", "").Replace(".mpls", "") + ".mkv")
                }
                else
                {
                    Add-XML-Attribute $_ "OutputName" (([string]$_.Source).Replace(".m2ts", "").Replace(".mpls", "") + "-" + ([string](([int]$a.SourceList) + 1)).PadLeft(2,'0') + ".mkv")
                }
            }
        }
        else
        {
            if (([Object[]]$PlayLists).count -eq 1)
            {
                if ($CanBe)
                {
                    Add-XML-Attribute $_ "OutputName" ($CanBe + "-" + $_.SourceFileName.Replace(".mpls", "") + ".mkv")
                }
                else
                {
                    Add-XML-Attribute $_ "OutputName" ("00-" + $_.SourceFileName.Replace(".mpls", "") + ".mkv")
                }
            }
            else
            {
                Add-XML-Attribute $_ "OutputName" ($_.SourceFileName.Replace(".mpls", "") + ".mkv")
            }
        }
    }
    $a = ($DiscData.SelectNodes("Title[not(@Delete)]") | Sort-Object @{Expression={$_.Names.Name[0].Ext}; Descending=$true}, OutputName)
}
else
{
    $DiscData.Title | foreach {
        $NewTitle = $_
        if ($_.Dummy)
        {
            Nuke-Title $Newtitle ""
        }
        else
        {
            if (([int]$NewTitle.Duration.InSeconds) -lt $MinDuration)
            {
                Nuke-Title $Newtitle "Shorter Than Minimum Duration"
            }
            elseif ($MaxDuration -and (([int]$NewTitle.Duration.InSeconds) -gt $MaxDuration))
            {
                Nuke-Title $Newtitle "Longer Than Maximum Duration"
            }
            if (([int]$NewTitle.ChapterCount) -lt $MinChapters)
            {
                Nuke-Title $Newtitle "Not Enough Chapters"
            }
            if (-not $NewTitle.SelectNodes("Stream[@Audio]"))
            {
                Nuke-Title $Newtitle "No Audio Tracks"
            }
        }
    }
    $a = 1
    $DiscData.SelectNodes("Title[not(@Delete)]") | foreach {
        Add-XML-Attribute $_ "OutputName" ("Title {0:00}.mkv" -f $a)
        $a++
    }
    $a = $DiscData.SelectNodes("Title[not(@Delete)]")
}
if ($a)
    {
        Write-Line Main Gray "Done."

        Write-Pillar

        ([object[]]$a) | ForEach {
            Write-Line Main Black "Title:" $_.Number
            Write-Line Head Green "Duration:" (-5) -DarkGreen $_.Duration.Hours ":" $_.Duration.Minutes ":" $_.Duration.Seconds 45 -black "(" -darkgreen $_.Duration.InSeconds -black ")"
            Get-Title-Segments $_ -Order | foreach {
                if ($_.Duration)
                {
                    Write-Line Line Green "Segment" 9 $_.Number ":" 15 $_.Duration
                }
                else
                {
                    Write-Line Line Green "Segment" 9 $_.Number ":" 15 "Unknown"
                }
            }
            Write-Line Head Green "End Durations."
            Write-Line Head Magenta "Properties:"
            Write-Line Line Magenta "Output Name:" 22 $_.OutputName
            if ($_.ChapterCount)
            {
                Write-Line Line Magenta "Chapter Count:" 22 $_.ChapterCount
            }
            else
            {
                Write-Line Line Magenta "Chapter Count:" 22 "0"
            }
            Write-Line Head Magenta "End"
        }

        Write-Line Main Black "End Titles."

        Write-Pillar

        break
        $FileLast = ([Object[]]$a).Count

        #Prepare Output Folder
            $FolderNumber = 1
            if (-not ((Get-Item -Path $Dest -ErrorAction SilentlyContinue))) {[void](New-Item -Path $Dest -ItemType "directory")}
            while (Get-Item -Path ("{0}\Disc {1:00}" -f $Dest, $FolderNumber) -ErrorAction SilentlyContinue) { $FolderNumber++ }

            $OutPutDirectory="{0}\Disc {1:00}" -f $Dest, $FolderNumber
            if ($DiscData.Name) { $DiscName = $DiscData.Name }
            else { $DiscName = $DiscData.VolumeName }

            [void](New-Item -Path $OutPutDirectory -ItemType "directory" -ErrorAction SilentlyContinue)
            Add-Content -Path ("$OutPutDirectory\~" + ([RegEx]::Replace($DiscName, "[{0}]" -f ([RegEx]::Escape(-join [System.IO.Path]::GetInvalidFileNameChars())), '_'))) -Value ($DiscName + "`n" + $DiscData.VolumeName)
        #End
        
        $a | ForEach {
            $ThisTitle=$_
            $DoneLast = 0
            if ($FileLast -gt 1) { while ($a[$DoneLast].Number -ne $ThisTitle.Number) { $DoneLast++ } }
            $NewName = $_.OutputName
            Write-Line Main White ("Progess: {0} of {1}." -f ($DoneLast+1), $FileLast)
            Skip-Lines 1
            
            Write-Line Main Black "Saving Title: " $ThisTitle.Number
            Write-Line Head Green "Title Paths"
            Write-Line Line Green "Temporary Name" 18 ":" ([int]-2) ([int](0 - $OutPutDirectory.Length)) $ThisTitle.OutputFileName
            Write-Line Line Green "To Directory" 18 ":" ([int]-1) $OutPutDirectory "\"
            Write-Line Line Green "Final Name" 18 ":" ([int]-2) ([int](0 - $OutPutDirectory.Length)) $NewName
            Write-Line Head Green "Set."
            $ReDo=$SubtitlesRemoved=$NowSaving=$false
            if (-not ((Get-Item -Path $OutPutDirectory -ErrorAction SilentlyContinue))) {[void](New-Item -Path $OutPutDirectory -ItemType "directory")}
            &$MakeMKVCon "--noscan" "-r" "--messages=-stdout" "--progress=-stdout" $ProfileCommand "--minlength=0" "mkv" "disc:$Disc" $ThisTitle.Number $OutPutDirectory | ForEach-Object -Process {

                $Type = $Line = $Temp = ""
                ([string] $_).Split(":", 2) | foreach {if ($Type) {$Line = $_} else {$Type = $_}}

                $EscapeOn = $InQuotes = $false
                $Params = @()
                $Line.ToCharArray() | foreach {
                    switch ($_)
                    {
                        '\' { if (-not $EscapeOn -and $InQuotes) { $EscapeOn = $true; break } }
                        ',' { if (-not $InQuotes) { $Params+=$Temp; $Temp=""; break } }
                        '"' { if (-not $EscapeOn) { $InQuotes = -not $InQuotes;  break } }
                        default { $Temp+=$_; $EscapeOn = $false }
                    }
                }
                $Params+=$Temp

                switch -wildcard ($Type)
                {
                    "MSG"
                    {
                        switch ([convert]::ToInt32($Params[0]))
                        {
                            $Message_DEBUG { if ($NowSaving) { Write-Line Line Red $Params[3]; break }}
                            $Message_DEBUG_LOG { break }
                            $Message_APP_START { $host.ui.RawUI.WindowTitle = $Params[5]  ;  break }
                            $Message_BUP_NOT_EQU_IFO { break }
                            $Message_PATH_NOT_EXIST { "Fatal Error: Destination Path Not Found."  ;  pause  ;  break }
                            $Message_OPENING_FILES { break }
                            $Message_FORCED_SUBTITLES_REMOVED {if ($SubtitlesRemoved) {$SubtitlesRemoved+=",{0}" -f $Params[5]} else {$SubtitlesRemoved=$Params[5]}; break }
                            $Message_AV_SYNC_ISSUES_FOUND { Write-Line Head Cyan "Av Sync Issues:" }
                            $Message_AV_SYNC_ISSUE { Write-Line Line Cyan -Magenta "@" -White "[" -Yellow $Params[6] -None "] Stream " -Green $Params[5] -None " - " -Gray $Params[8]; break }
                            $Message_QUERY_OVERWRITE_FILE { break }
                            $Message_SAVE_FAILED { Write-Line Main Black "Fatal Error: Failed to Complete Processing."  ;  pause  ;  break }
                            $Message_SAVE_PART_SUCCESSFUL { break }
                            $Message_SAVE_SUCCESSFUL { break }
                            $Message_OPERATION_SUCCESSFUL { if (-not $NowCompleted) { $NowCompleted = $true; break } }
                            $Message_SAVING_TITLES { if (-not $NowSaving) { $NowSaving = $true; break } }
                            $Message_COMPLETE_SUCCESSFUL
                            {
                                if ($SubtitlesRemoved) { Write-Line Line DarkGray "Empty Tracks [" $SubtitlesRemoved "] Removed." }
                                if ($ReDo)
                                {
                                    if (-not ((Get-Item -Path ($OutPutDirectory + "\ERROR") -ErrorAction SilentlyContinue))) {[void](New-Item -Path ($OutPutDirectory + "\ERROR") -ItemType "directory")}
                                    Move-Item ("{0}\{1}" -f $OutPutDirectory, $ThisTitle.OutputFileName) ($OutPutDirectory + "\ERROR")
                                    Write-Line Line Red "Moved Broken File To ERROR Folder."
                                    Write-Line Head Yellow "Failed."
                                }
                                else
                                {
                                    Write-Line Head Yellow "Renaming" 18 ":"
                                    Write-Line Line Yellow "From" 18 ":" ([int]-1) $ThisTitle.OutputFileName
                                    Write-Line Line Yellow "To" 18 ":" ([int]-1) $NewName
                                    Rename-Item -Path ("{0}\{1}" -f $OutPutDirectory, $ThisTitle.OutputFileName) -NewName $NewName
                                    if ($MKVPropEdit) { &$MKVPropEdit "-e" "info" "-s" ("title=SegmentsMap:" + $ThisTitle.SegmentsMap) ("{0}\{1}" -f $OutPutDirectory, $NewName) | Out-Null }
                                    if(!$?) { Write-Line Line Red "MKVPropEdit Error."; pause }
                                    Write-Line Head Yellow "Successful."
                                }
                                Write-Line Main Black "Done."
                                Skip-Lines 1
                                break
                            }
                            $Message_COMPLETE_PART_SUCCESSFUL { break }
                            $Message_DISC_FAILED { Write-Line Line Red "Failed to Open Disc." }
                            $Message_LIBAV_TRACE_ERROR { Write-Line Line Red "LibAV Trace Error."; break }
                            $Message_SCSI_ERROR { Write-Line Line Red "SCSI Error."; break }
                            $Message_LOAD_HASH_TABLE { break }
                            $Message_TITLE_WITH_CELLS_ADDED { break }
                            $Message_TITLE_ADDED { break }
                            $Message_TITLE_EQUAL { break }
                            $Message_DIRECT_DISC_ACCESS { break }
                            $Message_CORRUPTION_IN_SOURCE { Write-Line Line Red "Corruption In Source."; $ReDo=$true; break }
                            default { Write-Line Line DarkMagenta "Unknown Message (" $Params[0] "): " $Params[3]; Add-Content -Path "F:\Work\MakeMKV.log" -Value $_ ;  break }
                        }
                    }
                    "PRGT" { $ProgressTName = $Params[2]  ;  break }
                    "PRGC" { $ProgressCName = $Params[2]  ;  break }
                    "PRGV"
                    {
                        $ProgressCVal = _I $Params[0]  ;  $ProgressTVal = _I $Params[1]  ;  $ProgressMax = _I $Params[2] ; $ProgressDiv = $ProgressTVal / $ProgressMax
                        Write-Progress -activity "Total Progress" -Status ("Processing Title: {0} of {1}" -f ($DoneLast + 1), $FileLast) -CurrentOperation ("{0} -> {1}" -f $ThisTitle.SourceFileName, $ThisTitle.OutputFileName) -Id 0 -PercentComplete ((($DoneLast / $FileLast) + ((1 / $FileLast) * $ProgressDiv)) * 100) -ParentId -1
                        Write-Progress -activity "Current Progress" -Status $ProgressCName -CurrentOperation ("{0:p} Completed" -f $ProgressDiv) -Id 1 -PercentComplete ($ProgressDiv * 100) -ParentId 0
                        break
                    }
                    default { if ($Type -ne "DRV") { $_ }  ;  break }
                }
            }
        }
    }
    else
    {
        Write-Line Head Red "No Titles Left To Rip."
        Write-Line Main White "Done."
    }
    $DiscRecorder.EjectMedia()
    Start-Sleep -Seconds 1
}
It needs a lot of work...
Last edited by ndjamena on Sat Jan 02, 2016 1:43 pm, edited 14 times in total.

ndjamena
Posts: 830
Joined: Mon Jan 07, 2013 12:23 am

Re: Fun with Powershell And TV on Blu Ray

Post by ndjamena » Fri Dec 18, 2015 3:21 am

Separating MakeMKV functions into its own post.

"MakeMKVFunctions.ps1"

Code: Select all

[void]([System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms"))
[void]([System.Reflection.Assembly]::LoadWithPartialName("System.Drawing"))

$Attributes = @(
    "Unknown",
    "Type",
    "Name",
    "LangCode",
    "LangName",
    "CodecId",
    "CodecShort",
    "CodecLong",
    "ChapterCount",
    "Duration",
    "DiskSize",
    "DiskSizeBytes",
    "StreamTypeExtension",
    "Bitrate",
    "AudioChannelsCount",
    "AngleInfo",
    "SourceFileName",
    "AudioSampleRate",
    "AudioSampleSize",
    "VideoSize",
    "VideoAspectRatio",
    "VideoFrameRate",
    "StreamFlags",
    "DateTime",
    "OriginalTitleId",
    "SegmentsCount",
    "SegmentsMap",
    "OutputFileName",
    "MetadataLanguageCode",
    "MetadataLanguageName",
    "TreeInfo",
    "PanelTitle",
    "VolumeName",
    "OrderWeight",
    "OutputFormat",
    "OutputFormatDescription",
    "SeamlessInfo",
    "PanelText",
    "MkvFlags",
    "MkvFlagsText",
    "AudioChannelLayoutName",
    "OutputCodecShort",
    "OutputConversionType",
    "OutputAudioSampleRate",
    "OutputAudioSampleSize",
    "OutputAudioChannelsCount",
    "OutputAudioChannelLayoutName",
    "OutputAudioChannelLayout",
    "OutputAudioMixDescription"
)

function Set-Const ([string]$MSG, [int]$VAL) { Set-Variable $MSG -Value $VAL -Option Constant -Scope 1 -ErrorAction SilentlyContinue }
.{
    Set-Const Message_DEBUG_ASSERT             1001
    Set-Const Message_LIBAV_TRACE_ERROR        1002
    Set-Const Message_DEBUG                    1003
    Set-Const Message_DEBUG_LOG                1004
    Set-Const Message_APP_START                1005
    Set-Const Message_SCSI_ERROR               2003
    Set-Const Message_HDD_TOO_SLOW             2008
    Set-Const Message_PATH_NOT_EXIST           2019
    Set-Const Message_BUP_NOT_EQU_IFO          3002
    Set-Const Message_OPENING_FILES            3006
    Set-Const Message_DIRECT_DISC_ACCESS       3007
    Set-Const Message_TITLE_TOO_SMALL          3025
    Set-Const Message_TITLE_WITH_CELLS_ADDED   3028
    Set-Const Message_AACS_NOT_PRESENT         3305
    Set-Const Message_TITLE_ADDED              3307
    Set-Const Message_TITLE_EQUAL              3309
    Set-Const Message_DECRYPTOR_DETECTED       3334
    Set-Const Message_FORCED_SUBTITLES_REMOVED 4001
    Set-Const Message_CORRUPTION_IN_SOURCE     4004
    Set-Const Message_AV_SYNC_ISSUES_FOUND     4007
    Set-Const Message_AV_SYNC_ISSUE            4008
    Set-Const Message_AV_SYNC_ISSUES_COUNT     4047
    Set-Const Message_FILE_EXISTS              5001
    Set-Const Message_SAVE_FAILED              5003
    Set-Const Message_SAVE_PART_SUCCESSFUL     5004
    Set-Const Message_SAVE_SUCCESSFUL          5005
    Set-Const Message_DISC_FAILED              5010
    Set-Const Message_OPERATION_SUCCESSFUL     5011
    Set-Const Message_SAVING_TITLES            5015
    Set-Const Message_COMPLETE_SUCCESSFUL      5036
    Set-Const Message_COMPLETE_PART_SUCCESSFUL 5037
    Set-Const Message_LOAD_HASH_TABLE          5085
}

function Get-MakeMKVCon
{
    if (-not ($MakeMKVPath = ${env:ProgramFiles(x86)})) { $MakeMKVPath = ${env:ProgramFiles} }
    if ($MakeMKVPath=(Get-item -Path ($MakeMKVPath + "\MakeMKV") -ErrorAction SilentlyContinue).FullName)
    {
        if ($ENV:PROCESSOR_ARCHITECTURE -eq "AMD64")
        {
            if (-not ($MakeMKVCon = (Get-Item -Path ($MakeMKVPath + "\MakeMKVCon64.exe") -ErrorAction SilentlyContinue).FullName))
            {
                if (-not ($MakeMKVCon = (Get-Item -Path ($MakeMKVPath + "\MakeMKVCon.exe") -ErrorAction SilentlyContinue).FullName)) { break }
            }
        }
        else
        {
            if (-not ($MakeMKVCon = (Get-Item -Path ($MakeMKVPath + "\MakeMKVCon.exe") -ErrorAction SilentlyContinue).FullName)) { break }
        }
    }
    $MakeMKVCon
}

function Get-MKVPropEdit
{
    if (-not ($MKVPropEdit = Get-Item -Path (${env:ProgramFiles(x86)} + "\MKVToolNix\MKVPropEdit.exe") -ErrorAction SilentlyContinue))
    {
        $MKVPropEdit = Get-Item -Path (${env:ProgramFiles} + "\MKVToolNix\MKVPropEdit.exe") -ErrorAction SilentlyContinue
    }
    if ($MKVPropEdit) { $MKVPropEdit.FullName }
}

Function Get-FileName($InitialDirectory)
{   
 $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
 $OpenFileDialog.initialDirectory = $InitialDirectory
 $OpenFileDialog.filter = "All files (*.xml)| *.xml"
 [void]$OpenFileDialog.ShowDialog()
 $OpenFileDialog.filename
}

function Convert-Timecode-To-Base-Duration ([string]$TimeCode, [switch]$AsSeconds, [switch]$AsMilliseconds, [switch]$AsNanoSeconds, [switch]$AsTimeCode, [switch]$FromBase, [switch]$FromMilliSeconds, [switch]$FromSeconds, [switch]$FromNanoSeconds)
{
    $BaseDuration=1000000000
    if ($AsSeconds) { $BaseDuration=1 }
    if ($AsMilliSeconds) { $BaseDuration=1000 }
    if ($AsNanoSeconds) { $BaseDuration=1000000000 }

    if ($FromBase)
    {
        $TimeCode = [convert]::ToDouble($TimeCode) / $BaseDuration
    }
    elseif ($FromMilliSeconds)
    {
        $TimeCode = [convert]::ToDouble($TimeCode) / 1000
    }
    elseif ($FromNanoSeconds)
    {
        $TimeCode = [convert]::ToDouble($TimeCode) / 1000000000
    }
    elseif ($FromSeconds)
    {
        $TimeCode = [convert]::ToDouble($TimeCode) / 1
    }

    $BaseTime = $TimeCode.Split(':', [StringSplitOptions]::None)

    $Fraction=$Seconds=$Minutes=$Hours=0

    if ($BaseTime.Count -eq 3)
    {
        $Hours = [convert]::ToDouble($BaseTime[0])
    }
    if ($BaseTime.Count -ge 2)
    {
        $Minutes = [convert]::ToDouble($BaseTime[$BaseTime.Count - 2])
    }
    if ($BaseTime.Count -ge 1)
    {
        $Seconds = $BaseTime[$BaseTime.Count - 1].Split('.', [StringSplitOptions]::None)
        if ($Seconds.Count -ge 2)
        {
            $Fraction=[convert]::ToDouble(("0." + $Seconds[$Seconds.Count - 1]))
            $Fraction*=$BaseDuration
            if (($Fraction - [math]::Floor($Fraction)) -ge 0.5) { $Fraction+=1.0 }
            $Fraction=[int64][math]::Floor($Fraction)
        }
        $Seconds=[convert]::ToDouble($Seconds[0])
        if ($Fraction -eq $BaseDuration) { $Seconds++; $Fraction=0 }
    }
    if (($Hours -and $minutes -ge 60) -or ($Minutes -and $Seconds -ge 60))
    {
        #Error
    }
    if ($Seconds -ge 60) { $Minutes+=[math]::Floor($Seconds/60.0); $Seconds=$Seconds-($Minutes * 60) }
    if ($Minutes -ge 60) { $Hours+=[math]::Floor($Minutes/60.0); $Minutes=$Minutes-($Hours * 60) }
    if ($AsTimeCode)
    {
        if ($BaseDuration -gt 1)
        {
            ([string]$Hours).PadLeft(2,'0') + ":" + ([string]$Minutes).PadLeft(2,'0') + ":" + ([string]$Seconds).PadLeft(2,'0') + "." + ([string]$Fraction).PadLeft((([string]$BaseDuration).Length - 1),'0')
        }
        else
        {
            ([string]$Hours).PadLeft(2,'0') + ":" + ([string]$Minutes).PadLeft(2,'0') + ":" + ([string]$Seconds).PadLeft(2,'0')
        }
    }
    else
    {
        ($Hours * 60*60*$BaseDuration) + ($Minutes * 60*$BaseDuration) + ($Seconds * $BaseDuration) + $Fraction
    }
}

function Split-String ([string]$LineString, [int]$PartSize, [int]$FirstSize=0)
{
    if ($FirstSize -and $LineString.Length -ge $FirstSize)
    {
        $LineString.Remove($FirstSize)
        $LineString = $LineString.Remove(0, $FirstSize)
    }
    for (;;)
    {
        if ($LineString.Length -le $PartSize)
        {
            if ($LineString.Length) { $LineString }
            break
        }
        $LineString.Remove($PartSize)
        $LineString = $LineString.Remove(0, $PartSize)
    }
}

function Test-Colour ([string]$IsColour)
{
    [enum]::GetValues([System.ConsoleColor]) | foreach { if ($IsColour -ieq $_) { [string]$_; return } }
    switch ($IsColour)
    {
        "grey"
        {
            "Gray"
            break
        }
        "darkgrey"
        {
            "DarkGray"
            break
        }
        "aqua"
        {
            "Cyan"
            break
        }
        "darkaqua"
        {
            "DarkCyan"
            break
        }
        "pink"
        {
            "Magenta"
            break
        }
        "purple"
        {
            "DarkMagenta"
            break
        }
        "none"
        {
            "None"
            break
        }
    }
}

function Test-LineType ([string]$IsLineType)
{
    switch ($IsLineType)
    {
        "main"
        {
            "Main"
            return
        }
        "line"
        {
            "Line"
            return
        }
        "head"
        {
            "Head"
            return
        }
    }
}

function Write-Part ([string]$LineString, [string]$ForeGroundColour = "None", [string]$BackGroundColour = "None", [switch]$NewLine)
{
    if ($ForeGroundColour -eq "None")
    {
        if ($BackGroundColour -eq "None")
        {
            if ($NewLine)
            {
                Write-Host $LineString
            }
            else
            {
                Write-Host -NoNewline $LineString
            }
        }
        else
        {
            if ($NewLine)
            {
                Write-Host -b $BackGroundColour $LineString
            }
            else
            {
                Write-Host -b $BackGroundColour -NoNewline $LineString
            }
        }
    }
    else
    {
        if ($BackGroundColour -eq "None")
        {
            if ($NewLine)
            {
                Write-Host -f $ForeGroundColour $LineString
            }
            else
            {
                Write-Host -f $ForeGroundColour -NoNewline $LineString
            }
        }
        else
        {
            if ($NewLine)
            {
                Write-Host -f $ForeGroundColour -b $BackGroundColour $LineString
            }
            else
            {
                Write-Host -f $ForeGroundColour -b $BackGroundColour -NoNewline $LineString
            }
        }
    }
}

function Write-Line ([string]$LineType, [string]$LineColour)
{
    if ($LineType)
    {
        $Test = Test-LineType $LineType
    }
    else
    {
        $Test = $null
    }
    if (!$Test)
    {
        Write-Line Line $LineType $LineColour $args
        return
    }
    $LineType=$Test
    if ($LineColour)
    {
        $Test = (Test-Colour $LineColour)
    }
    else
    {
        $Test = $null
    }
    if (!$Test)
    {
        Write-Line $LineType None $LineColour $args
        return
    }
    $LineColour = $Test
    $BackGroundColour=$ForegroundColour="None"
    switch -wildcard ($LineType)
    {
        *e*
        {
            if (-not $MainColour )
            {
                Set-Variable -Scope "Script" -Name MainColour -Value None
            }
            Write-Part " " -BackGroundColour $MainColour
        }
        "Line"
        {
            $Offset=2
            Write-Part " " -BackGroundColour $LineColour
            break            
        }
        "Head"
        {
            $Offset=1
            $BackGroundColour=$LineColour
        }
        "Main"
        {
            $Offset=0
            $BackGroundColour=$LineColour
            Set-Variable -Scope "Script" -Name MainColour -Value $LineColour
        }
        *a*
        {
            if ($LineColour -eq "Black" -or ($LineColour.StartsWith("Dark", $true, $null) -and $LineColour -ne "DarkYellow") -or $LineColour -eq "Blue" -or $LineColour -eq "None")
            {
                $ForeGroundColour="White"
            }
            else
            {
                $ForeGroundColour="Black"
            }
            break
        }
    }
    $LineSum=$Offset
    $args | foreach {
        if ($_) { $ARG = $_ }
        else { $ARG = "" }
        switch ($ARG.GetType().Name)
        {
            "String"
            {
                $ARG=[string]$ARG
                Switch -wildcard ($ARG)
                {
                    -*
                    {
                        $Test = Test-Colour $ARG.Remove(0,1)
                        if ($Test) { $ForeGroundColour=$Test; break }
                    }
                    *-
                    {
                        $Test = Test-Colour $ARG.Remove($ARG.Length - 1)
                        if ($Test) { $BackGroundColour=$Test; break }
                    }
                    *
                    {
                        $Test = $false;
                        $ARG.Split(' ', [StringSplitOptions]::None) | foreach {
                            $LineString=([string]$_)
                            if ($Test) {
                                if ($LineType -eq "Line")
                                {
                                    if ($ForegroundColour -eq "DarkGray")
                                    {
                                        Write-Part "." -ForegroundColour Gray -BackgroundColour DarkGray
                                    }
                                    else
                                    {
                                        Write-Part "." -ForegroundColour DarkGray -BackgroundColour $BackgroundColour
                                    }
                                    $LineSum++
                                }
                                else
                                {
                                    Write-Part " " -BackGroundColour $BackgroundColour
                                    $LineSum++
                                }
                            }
                            Split-String $LineString (131 - $Offset) (131 - $LineSum) | foreach {
                                $LineString=([string]$_)
                                if ($LineString.length -gt (131 - $LineSum))
                                {
                                    if ($LineType -eq "Line")
                                    {
                                        Write-Part ("."*(131 - $LineSum)) -ForeGoundColour DarkGray
                                        $LineSum = 131
                                    }
                                    Write-Part (" "*(132 - $LineSum)) -BackGroundColour $LineColour -NewLine
                                    $LineSum=0
                                    switch -wildcard ($LineType)
                                    {
                                        *e* {
                                            Write-Part " " -BackGroundColour $MainColour
                                            $LineSum++
                                        }
                                        "Line" {
                                            Write-Part " " -BackGroundColour $LineColour
                                            $LineSum++
                                            break            
                                        }
                                    }
                                }
                                Write-Part $_ -BackGroundColour $BackgroundColour -ForeGroundColour $ForeGroundColour
                                $LineSum+=$_.Length
                                $Test = $true
                            }
                        }
                    }
                }
                break
            }
            "Int32"
            {
                if ($ARG -lt 0) { $ARG = $LineSum + [math]::Abs($ARG) }
                elseif ($ARG -le $LineSum) { $ARG=$LineSum+1 }
                if ($LineType -ieq "Main") { Write-Part (" "*($ARG-$LineSum)) -BackGroundColour $LineColour }
                elseif ($LineType -ieq "Head") { Write-Part ("."*($ARG-$LineSum)) -ForGroundColour DarkGray -BackGroundColour $LineColour }
                else { Write-Part ("."*($ARG-$LineSum)) -ForeGroundColour DarkGray }
                $LineSum=$ARG
                break
            }
            default
            {
                $_.GetType().Name
            }
        }
    }
    switch -wildcard ($LineType)
    {
        *a*
        {
            Write-Part (" "*(132 - $LineSum)) -BackGroundColour $LineColour -NewLine
            break
        }
        "Line"
        {
            Write-Part ("."*(131 - $LineSum)) -ForeGroundColour DarkGray
            Write-Part " " -BackgroundColour $LineColour -NewLine
            break            
        }
    }
}
Function Get-Title-Segments
(
    [Parameter(Position=0, ParameterSetName="Element")]
    [System.Xml.XmlElement]$TitleNode, 
    [Parameter(Position=0, ParameterSetName="Number")]
    [int32]$TitleNumber,
    [Parameter(Position=1)]
    [int32]$RemoveSegment=-1,
    [Parameter(Position=2)]
    [switch]$Order,
    [switch]$Unique
)
{
    if ($PSCmdlet.ParameterSetName -eq "Element") { $TitleNumber=$TitleNode.Number }
    
    if (-not $Order)
    {
        $DiscData.SelectNodes("Segment/Title[@Number=$TitleNumber]") | foreach { if ($_.ParentNode.Number -ne $RemoveSegment) {$_.ParentNode} }
    }
    else
    {
        if ($PSCmdlet.ParameterSetName -eq "Number" -or (-not $TitleNode.SegmentsMap)) { $TitleNode=$DiscData.SelectSingleNode(("Title[@Number=$TitleNumber]")) }
        $a = @()
        $TitleNode.SegmentsMap.Split(',') | foreach { if (($_ -ne $RemoveSegment -and $_ -ne "...") -and (-not ($Unique -and $a.Contains($_)))) { if ($b = $DiscData.SelectSingleNode(("Segment[@Number=$_]"))) { $a+=$_; $b } } }
    }
}
Function Get-Segment-Titles (
    [Parameter(Position=0, ParameterSetName="Element")]
    [System.Xml.XmlElement]$SegmentNode,
    [Parameter(Position=0, ParameterSetName="Number")]
    [int32]$SegmentNumber,
    [Parameter(Position=1)]
    [int32]$RemoveTitle=-1,
    [Parameter(Position=2)]
    [switch]$Order
)
{
    if ($PSCmdlet.ParameterSetName -eq "Element") { $SegmentNumber=$SegmentNode.Number }
    if ($PSCmdlet.ParameterSetName -eq "Number" -or (-not $SegmentNode.Title)) { $SegmentNode=$DiscData.SelectSingleNode(("Segment[@Number=" + $SegmentNumber + "]")) }

    if (-not $Order)
    {
        $SegmentNode.Title | foreach { if ($_.Number -ne $RemoveTitle) { $DiscData.SelectSingleNode(("Title[@Number=" + $_.Number + "]")) } }
    }
    else
    {
        $SegmentNode.Title | foreach { if ($_.Number -ne $RemoveTitle) { $DiscData.SelectSingleNode(("Title[@Number=" + $_.Number + "]")) } } | foreach { $Title=$_; ([string]$_.SegmentsMap).Split(',') | foreach { if ($_ -eq $SegmentNumber) { $Title } } } | Sort-Object { [int]$_.Number }
    }
}
function _I ($v)
{
    [int][convert]::ToInt32($v)
}
function Write-Pillar {
    [CmdletBinding(DefaultParameterSetName="Number")]
    param (
        [Parameter(Position=0, ParameterSetName="String")]
        [String]$PillarString="Number",
        [Parameter(Position=0, ParameterSetName="Number")]
        [int32]$PillarSize=132,
        [switch]$Contrast
    )
    PROCESS{
    
        if ($PSCmdlet.ParameterSetName -eq "String")
        {
            switch ($PillarString)
            {
                "" { $Pillarsize=132; break }
                "Full" { $Pillarsize=132; break }
                "Half" { $Pillarsize=64; break }
                "Third" { $PillarSize=44; break }
                "Quarter" { $PillarSize=33; break }
                default { if (-not $PillarSize) { if ( -not ($PillarSize=_I $PillarString)) { $PillarSize=132 } }; break }
            }
        }
        if ($Contrast) {Write-Host ("`n`n" + ("-"*$PillarSize)) -f White -b Black -n}
        else {Write-Host ("`n`n" + ("-"*$PillarSize)) -n}
        Write-Host "-`n`n" -b White -f Black
}
}
Function Get-FolderName
{
    $objShell = new-object -com shell.application
    $objFolder = $objShell.NameSpace("DeskTop")
    ($objShell.BrowseForFolder(0,"Please select destination folder:",0,12)).self.path
}
Function Get-Dest ( [string]$Source, [string]$Message, [string[]]$NewDestList, [switch]$DoMulti, [switch]$InMulti)
{
    $DestChoice = @()
    if (-not $NewDestList) { if (Get-Item -path $Source -ErrorAction SilentlyContinue) { $NewDestList = @(Get-Content $Source) }}
    $DestList = {$NewDestList}.Invoke()
    if ($DestList) { $DestList | foreach { $DestChoice += New-Object System.Management.Automation.Host.ChoiceDescription ($_) } }
    if ($InMulti) { $DestChoice += New-Object System.Management.Automation.Host.ChoiceDescription ("End") }
    elseif ($DoMulti) { $DestChoice += New-Object System.Management.Automation.Host.ChoiceDescription ("Multi") }
    $DestChoice += New-Object System.Management.Automation.Host.ChoiceDescription ("New")
    $Dest = $host.ui.PromptForChoice("Output", "Choose Destination Folder.", $DestChoice, 0)
    if (($Dest + 1 ) -eq $DestChoice.Length) {
        if ($Dest = Get-FolderName) {
            $Test=$true
            $DestList | foreach {if ([io.path]::GetFullPath($Dest) -eq [io.path]::GetFullPath($_)) {$Test = $false}}
            if ($Test) {[void](Add-Content -Path $Source -Value $Dest)}
            $Dest
        }
        if ($InMulti) { (Get-Dest $Source $Message $DestList -InMulti) }
    }
    elseif (($DoMulti -or $InMulti) -and (($Dest + 2) -eq $DestChoice.Length)) {
        if (-not $InMulti) { (Get-Dest $Source $Message $DestList -InMulti) }
    }
    else { $DestChoice[$Dest].Label; [void]$DestList.Remove($DestList[$Dest]); if ($InMulti) { (Get-Dest $Source $Message $DestList -InMulti) } }
}
function Get-Number ([string] $Type, [string]$KeyWord)
{
    if ($KeyWord -eq "Time")
    {
        $Time=$true
    }
    else
    {
        $Time=$false
    }
    $objForm = New-Object System.Windows.Forms.Form
    $objForm.Text = "Enter $Type"
    $objForm.Size = New-Object System.Drawing.Size(300,150)
    $objForm.StartPosition = "CenterScreen"

    $objForm.KeyPreview = $True
    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Enter")
        {if ($objTextBox.Text) {Set-Variable -Scope "Global" -Name Output -Value  ([int]::Parse($objTextBox.Text))};$objForm.Close()}})
    $objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
        {$objForm.Close()}})

    if ($Time)
    {
        $SecondsButton = New-Object System.Windows.Forms.Button
        $SecondsButton.Location = New-Object System.Drawing.Size(20,70)
        $SecondsButton.Size = New-Object System.Drawing.Size(75,23)
        $SecondsButton.Text = "Seconds"
        $SecondsButton.Add_Click({if ($objTextBox.Text) {Set-Variable -Scope "Global" -Name Output -Value ([int]::Parse($objTextBox.Text))};$objForm.Close()})
        $objForm.Controls.Add($SecondsButton)
    }

    $MinutesButton = New-Object System.Windows.Forms.Button
    $MinutesButton.Location = New-Object System.Drawing.Size(102,70)
    $MinutesButton.Size = New-Object System.Drawing.Size(75,23)
    if ($Time)
    {
        $MinutesButton.Text = "Minutes"
    }
    else
    {
        $MinutesButton.Text = $KeyWord
    }
    $MinutesButton.Add_Click({if ($objTextBox.Text) {Set-Variable -Scope "Global" -Name Output -Value ([int]::Parse($objTextBox.Text) * (1 + (([int]$Time) * 59)))} ;$objForm.Close()})
    $objForm.Controls.Add($MinutesButton)

    if ($Time)
    {
        $HoursButton = New-Object System.Windows.Forms.Button
        $HoursButton.Location = New-Object System.Drawing.Size(185,70)
        $HoursButton.Size = New-Object System.Drawing.Size(75,23)
        $HoursButton.Text = "Hours"
        $HoursButton.Add_Click({if ($objTextBox.Text) {Set-Variable -Scope "Global" -Name Output -Value ([int]::Parse($objTextBox.Text) * 60 * 60)} ;$objForm.Close()})
        $objForm.Controls.Add($HoursButton)
    }

    $objLabel = New-Object System.Windows.Forms.Label
    $objLabel.Location = New-Object System.Drawing.Size(10,20) 
    $objLabel.Size = New-Object System.Drawing.Size(280,20) 
    $objLabel.Text = "Enter $Type`:"
    $objForm.Controls.Add($objLabel) 

    $objTextBox = New-Object System.Windows.Forms.TextBox 
    $objTextBox.Location = New-Object System.Drawing.Size(10,40) 
    $objTextBox.Size = New-Object System.Drawing.Size(260,20) 
    $objTextBox.Text=$New
    $objTextBox.SelectionStart=$SStart
    $objTextBox.SelectionLength=$NLen
    $objTextBox.Modified = $false

    $objTextBox.Add_KeyDown({if ((-not (($_.KeyValue -ge 48 -and $_.KeyCode -le 57) -or ($_.KeyValue -ge 96 -and $_.KeyCode -le 105))) -and $_.KeyValue -ne 37 -and $_.KeyValue -ne 39 -and $_.KeyValue -ne 46 -and $_.KeyValue -ne 8) { Write-Host $_.KeyValue; $_.SuppressKeyPress=$true } })
    function mods {
        $New = ""
        $SStart = 0
        $NStart = -1
        $NLen = 0
        $CStart=0
        $objTextBox.Text.ToCharArray() | foreach -process {
            if ($_ -ge '0' -and $_ -le '9')
            {
                $New+=$_
                $SStart+=1
            }
            $CStart+=1
            if ($CStart -eq $objTextBox.SelectionStart)
            {
                $NStart = $SStart
                $SStart = 0
            }
            if ($NStart -ge 0)
            {
                if (($objTextBox.SelectionLength + $objTextBox.SelectionStart) -eq $CStart)
                {
                    $NLen = $SStart
                }
            }
        }
    }
    $objTextBox.Add_ModifiedChanged({mods $_})
    $objForm.Controls.Add($objTextBox) 

    $objForm.Topmost = $True


    $objForm.Add_Shown({$objForm.Activate()})
    [void] $objForm.ShowDialog()

    $Output
}
function Get-Duration ( [string] $Source, [string] $Type, [string]$KeyWord )
{
    $MinChoice = @()
    if (-not $NewMinList) { if (Get-Item -path $Source -ErrorAction SilentlyContinue) { $NewMinList = @(Get-Content $Source) } }
    $MinList = {$NewMinList}.Invoke()
    if ($MinList) { $MinList | foreach { $MinChoice += New-Object System.Management.Automation.Host.ChoiceDescription ($_) } }
    $MinChoice += New-Object System.Management.Automation.Host.ChoiceDescription ("None")
    $MinChoice += New-Object System.Management.Automation.Host.ChoiceDescription ("New")
    $Min = $host.ui.PromptForChoice("Output", "Choose $Type.", $MinChoice, 0)
    if (-not (($Min + 2) -eq $MinChoice.length))
    {
        if (($Min + 1) -eq $MinChoice.length)
        {
            $Min = [int](Get-Number $Type $KeyWord)
            if ($Min)
            {
                $Test=$true
                if ($MinList) { $MinList | foreach { if ($Min -eq [int]::Parse($_)) {$Test = $false} } }
                if ($Test) {[void](Add-Content -Path $Source -Value $Min)}
                $Min
            }
        }
        else
        {
            [int]::Parse($MinChoice[$Min].Label)
        }
    }
}
Function Get-Disc
{
    $DiscChoice = @()
    ($Discmaster = New-Object -ComObject IMAPI2.MsftDiscMaster2) | ForEach-Object {
        ($DiscRecorder=New-Object -ComObject IMAPI2.MsftDiscRecorder2).InitializeDiscRecorder($_)
        $DiscChoice+=New-Object System.Management.Automation.Host.ChoiceDescription ("&" + $DiscRecorder.VolumePathNames), ($DiscRecorder.ProductId + " [" + $DiscRecorder.LegacyDeviceNumber + "]")
    }
    $Disc = $host.ui.PromptForChoice("Optical Drives", "Choose Optical Drive to Process.", ($DiscChoice+=New-Object System.Management.Automation.Host.ChoiceDescription ("Cancel")), 0)
    if ($Disc -ne $Discmaster.count) {
        ($DiscRecorder=New-Object -ComObject IMAPI2.MsftDiscRecorder2).InitializeDiscRecorder($DiscMaster[$Disc])
        $DiscRecorder
    }
}
Function Get-Profile
{
    $ProfileChoice = @()
    if (Get-Item -path "PSProfileList.cfg" -ErrorAction SilentlyContinue) { $ProfileList = Get-Content "PSProfileList.cfg"; if ($ProfileList) { $ProfileList | foreach { $ProfileChoice += New-Object System.Management.Automation.Host.ChoiceDescription ($_) } } }
    $Profile = $host.ui.PromptForChoice("Output", "Choose Profile.", ($ProfileChoice += (New-Object System.Management.Automation.Host.ChoiceDescription ("Default")), (New-Object System.Management.Automation.Host.ChoiceDescription ("New"))), 0)
    if ($Profile -eq ($ProfileChoice.Length - 1)) {
        if (($Profile = Get-FileName)) {
            $Test=$true
            $ProfileList | foreach {if ([io.path]::GetFullPath($Profile) -eq [io.path]::GetFullPath($_)) {$Test = $false}}
            if ($Test) {Add-Content -Path "PSProfileList.cfg" -Value $Profile}
            $Profile
        }
    }
    elseif ($Profile -ne ($ProfileChoice.Length - 2)) { $ProfileChoice[$Profile].Label }
}
Function Skip-Lines ([int]$Count)
{ "`n"*$Count }
Function Add-XML-Attribute ([System.Xml.XmlElement]$Element, [String]$Name, [String]$Value, [int]$Return=0)
{
    
    ($NewAttribute = $Element.OwnerDocument.CreateAttribute($Name)).Value=$Value
    [void]$Element.SetAttributeNode($NewAttribute)
    switch ($Return)
    {
        0 { break }
        1 { $NewAttribute; break }
        3 { $Name; break }
        4 { $Value; break }
        default { $Element; break }
    }
}
Function Add-XML-Element ([System.Xml.XmlElement]$Element, [String]$Name, [int]$Return=1)
{
    $NewElement = $Element.AppendChild($Element.OwnerDocument.CreateElement($Name))
    switch ($Return)
    {
        1 { $NewElement; break }
        2 { $Element; break }
        3 { $Name; break }
        default { break }
    }
}
Function Get-XML-Element ([System.Xml.XmlElement]$Element, [String]$Name, [String]$Attribute, [String]$AttributeValue, [Switch]$Force)
{
    if ($Attribute) {
        if ($AttributeValue) { $Search="=""$AttributeValue""" }
        $Search = "[@$Attribute$Search]"
    }
    if (-not ($GetNode = $Element.SelectSingleNode($Name + $Search))) {
        if ($Force)
        {
            if ($Attribute) { Add-XML-Attribute (Add-XML-Element $Element $Name) $Attribute $AttributeValue 2 }
            else { Add-XML-Element $Element $Name }
        }
    }
    else { $GetNode }
}
Function Set-Times ( [System.Xml.XmlElement]$Title )
{
    $TimeCode = $Title.Duration
    if (($Time = ([string] $Title.Duration).Split(':')).Length -eq 3)
    {
        $Title.RemoveAttribute("Duration")
        $Duration = Add-XML-Element $Title "Duration"
        Add-XML-Attribute $Duration "InSeconds" ((60 * $Time[1]) + (3600 * $Time[0]) + $Time[2])
        Add-XML-Attribute $Duration "InMinutes" ((60 * $Time[0]) + $Time[1])
        Add-XML-Attribute $Duration "Seconds" $Time[2]
        Add-XML-Attribute $Duration "Minutes" $Time[1]
        Add-XML-Attribute $Duration "Hours" $Time[0]
        Add-XML-Attribute $Duration "TimeCode" $TimeCode
    }
}
Function Video-Standard ( [System.Xml.XmlElement]$Stream )
{
    $Standard = @("LD", "SD","MD","HD", "UHD")
    if ($Stream.VideoSize)
    {
            
        $Std=-1
        if (($Res = $Stream.VideoSize.Split('x')).Length -eq 2)
        {
            $Hor=[convert]::ToInt32($Res[0])
            $Ver=[convert]::ToInt32($Res[1])
            if ($Hor -le 512 -and $Ver -le 288) { $Std=0 }
            elseif ($Hor -le 1024 -and $Ver -le 576) { $Std=1 }
            elseif ($Hor -le 1280 -and $Ver -le 720) { $Std=2 }
            elseif ($Hor -le 1920 -and $Ver -le 1080) { $Std=4 }
            else { $Std=8 }

            [int] (Add-XML-Attribute $Stream "VideoStandard" $Std 4)
        }
    }
}
Function Enum-Streams ( [System.Xml.XmlElement]$Title, [String]$Type )
{
    $Unique = $Quality = $MaxQual = 0
    $Title.SelectNodes("Stream[@Type='$Type']") | foreach {
        $Stream=$_
            
        if (-not (($StreamFlags = _I $_.StreamFlags) -band (16384 + 2048 + 256))) {
            $Unique++
            Switch ($Type)
            {
                "Video" { $Quality=$Quality -bor (Video-Standard $Stream); break }
                "Audio"
                {
                    switch ($Stream.CodecShort)
                    {
                        "DTS-HD MA" { $Quality=($Quality -bor 16); if ($MaxQual -lt 16) { $MaxQual = 16 }; break }
                        "FLAC" { $Quality=($Quality -bor 16); if ($MaxQual -lt 16) { $MaxQual = 16 }; break }
                        "LPCM" { $Quality=($Quality -bor 16); if ($MaxQual -lt 16) { $MaxQual = 16 }; break }
                        "TrueHD" { $Quality=($Quality -bor 16); if ($MaxQual -lt 16) { $MaxQual = 16 }; break }
                        "DTS-HD HRA" { $Quality=($Quality -bor 8); if ($MaxQual -lt 8) { $MaxQual = 8 }; break }
                        "DTS" { $Quality=($Quality -bor 4); if ($MaxQual -lt 4) { $MaxQual = 4 }; break }
                        "DD" { $Quality=($Quality -bor 2); if ($MaxQual -lt 2) { $MaxQual = 2 }; break }
                        default { $Quality=($Quality -bor 1); if ($MaxQual -lt 1) { $MaxQual = 1 }; break }
                    }
                }
            }
        }
    }
    Add-XML-Attribute $Title "Unique$Type" $Unique
    if ($Type -eq "Audio" -or $Type -eq "Video") { Add-XML-Attribute $Title ($Type+"Quality") $Quality; Add-XML-Attribute $Title ($Type+"MaxQuality") $MaxQuality }
}
Function Compare-Segments ([String]$Segments1, [String]$Segments2)
{
    $Index = -1
    $Count = 0
    $Segments1 = ',' + $Segments1 + ','
    $Segments2.Split(',') | foreach {
        $NewIndex = $Segments1.IndexOf(("," + $_ + ","))
        if ($NewIndex -ge 0)
        {
            if ($NewIndex -gt $Index)
            { $Segments1 = $Segments1.Substring(0, $NewIndex + 1) + ("*"*$_.Length) + $Segments1.Substring($NewIndex + 1 + $_.Length)
                if ($Count -ge 0) {$Count++}
                else {$Count--}
            }
            else { $Count=-1-[math]::abs($Count) }
            $Index=$NewIndex
        }
    }
    $Count
}
function Enum-Unique-Segments ( [System.Xml.XmlElement]$Title )
{
    $SegmentsMap=","+$Title.SegmentsMap+","
    $Count=0
    $Title.SegmentsMap.Split(',') | foreach {
        if ($SegmentsMap.Contains(",$_,"))
        {
            do { $SegmentsMap=$SegmentsMap.Replace(','+$_+',', ',*,') } while ( $SegmentsMap.Contains(",$_,") )
            $Count++
        }
    }
    Add-XML-Attribute $Title "UniqueSegments" $Count
}
function Add-Delete-Request ( [int32]$Title, [int32]$Segment, [String]$Reason, [int32]$Cause, [int32]$Level, [switch]$Rename=$null )
{
    $DeleteRequest = Get-XML-Element $DeleteList "Title" "Number" $Title
    if (-not $DeleteRequest)
    {
        $DeleteRequest = Get-XML-Element $DeleteList "Title" "Number" $Title -Force
        Get-Title-Segments $Title $Segment | foreach { [void] (Get-XML-Element $DeleteRequest "Segment" "Number" $_.Number -Force) }
    }
    $DeleteRequest = Get-XML-Element $DeleteRequest "Segment" "Number" $Segment -Force
    $DeleteRequest = Add-XML-Element $DeleteRequest "DeleteRequest"
    Add-XML-Attribute $DeleteRequest "Reason" $Reason
    Add-XML-Attribute $DeleteRequest "Cause" $Cause
    Add-XML-Attribute $DeleteRequest "Level" $Level
    if ($Rename) { Add-XML-Attribute $DeleteRequest "Rename" $True; $Rename=$null }
}
function Nuke-Title ( [System.Xml.XmlElement]$Title, [string]$Reason, [string]$Extra)
{
        [void] (Set-Variable -Scope "Script" -Name Nuke_Used -Value $True)
        Write-Line Line White ( Get-Variable -Scope "Script" -Name Nuke_Colour ).Value "Removing: Title" ([int]-1) (([string]$Title.Number).PadLeft(([string]([object[]]$Title.ParentNode.Title).Count).Length)) "." ([int]-2) "Reason:" ([int]-1) $Reason "." ([int]-2) $Extra
        Add-XML-Attribute $Title "Delete" "True"
}
Last edited by ndjamena on Sat Dec 26, 2015 2:35 am, edited 6 times in total.

ndjamena
Posts: 830
Joined: Mon Jan 07, 2013 12:23 am

Re: Fun with Powershell And TV on Blu Ray

Post by ndjamena » Wed Dec 23, 2015 2:45 pm

I've actually run out of hard drive space, but the script in its current iteration actually does a bloody good job.

I still need to put more work into determining segment durations, and I haven't really touched the sorting of tracks yet but mostly I need to see about sorting out discs with the Play All split into multiple playlists and episodes with random "extra" mpls files that can throw off the ordering in some cases.

Other than writing the segment map into the MKV Segment Title I haven't touched incorporating segment numbers in the ordering routines.

What it does for now is ask MakeMKV for info about the disc, it saves all the info about the duplicate playlists, saves the title info, then reapplies the duplicate playlist numbers to the titles, then sorts all the selected and duplicate playlist numbers and changes the final names of the output file to reflect all of the playlists the title comes from.

Then it processes all the segments, checking each of the titles and trying to figure out the duration of each segment (that's something MakeMKV really ought to output in the first place) then it removes any segment of less than 3 seconds from further processing in title selection (each Star Trek TNG episode was separated by a 1 second segment that wasn't the same between the episode playlists and the play alls). Then it checks the segment maps of the titles looking for Play Alls and duplicate titles, removing duplicates that are chapterless m2ts files, Region A titles or Play Alls before applying both a minimum and maximum duration, it tries to guess any missing pieces of a Play All (TNG again but it needs work for titles that are added BEFORE the playlist, which I haven't seen yet, or titles whose numbers are sandwiched between two copies of the same playlist (which is where processing the segment numbers would come in handy), I should go rip Warehouse 13 again and see what those double playlist were about...) It also removes titles with no audio and might do other things I don't remember, then it sorts everything by name, rips the mpls files in order first and then whatever m2ts files are still in contention.

I had a disc with almost a thousand titles... that was a pain in the arse. I haven't figure out if there's a way to tell MakeMKV to rip certain titles in a certain order without having it rescan the entire disc before each title. When it takes several minutes to just scan the disc before each title just to rip some animated shorts it does become rather painful.

It would be nice to turn it into a proper GUI for MakeMKV using c# but that's a tad beyond me at the moment...
Last edited by ndjamena on Sun Dec 27, 2015 2:12 am, edited 1 time in total.

ndjamena
Posts: 830
Joined: Mon Jan 07, 2013 12:23 am

Re: Fun with Powershell And TV on Blu Ray

Post by ndjamena » Fri Dec 25, 2015 7:21 pm

This is what a problem disc looks like.

Code: Select all

Title:5                                                                                                                             
 Duration:.....0:43:50.......................(2630)                                                                                 
  Segment.3:...2630................................................................................................................ 
 End Durations.                                                                                                                     
 Properties:                                                                                                                        
  Output.Name:........00-00003-00216.mkv........................................................................................... 
  Chapter.Count:......4............................................................................................................ 
 End                                                                                                                                
Title:4                                                                                                                             
 Duration:.....0:43:46.......................(2626)                                                                                 
  Segment.0:...2626................................................................................................................ 
 End Durations.                                                                                                                     
 Properties:                                                                                                                        
  Output.Name:........01-00000-00226-00238.mkv..................................................................................... 
  Chapter.Count:......4............................................................................................................ 
 End                                                                                                                                
Title:7                                                                                                                             
 Duration:.....0:43:37.......................(2617)                                                                                 
  Segment.5:...2617................................................................................................................ 
 End Durations.                                                                                                                     
 Properties:                                                                                                                        
  Output.Name:........02-00005-00237-00239.mkv..................................................................................... 
  Chapter.Count:......4............................................................................................................ 
 End                                                                                                                                
Title:6                                                                                                                             
 Duration:.....0:43:46.......................(2626)                                                                                 
  Segment.4:...2626................................................................................................................ 
 End Durations.                                                                                                                     
 Properties:                                                                                                                        
  Output.Name:........01-00004.mkv................................................................................................. 
  Chapter.Count:......4............................................................................................................ 
 End                                                                                                                                
End Titles.                                                                                                                         
There's a Play All in there combining the first and last episodes on the disc (ie the ones with commentaries). Technically, there's a set of mpls numbers that are in the correct order, but they leave a gap between 00000 and 00003. Plus there are a bunch of extra mpls numbers in there, at least some of which reinforce the commentary Play All order. mpls 00001 consists of a 42 second segment, also numbered 00001, but there's no 00002.mpls on the disc.

The segment numbers are also technically in order, and their numbering mirrors that of the correctly ordered mpls files. Based on the existence of a Play All, and the numbering of three of the mpls files it would be highly likely any code I write to sort this stuff out would decide titles 4 and 7 are related in some way. Knowing how the disc is laid out I could just write in that mpls order + m2ts order overrides Play All order, or that orders that combine titles with similar track layouts should be taken with a grain of salt. But of course it's highly likely any code I add to guess the correct order for this particular disc would throw off the order for another disc that exists somewhere out there.

Oddly enough, the MakeMKV title numbers are also in order, even though that's the least reliable method of determining correct orders.

This would be why title ordering was never implemented in MakeMKV, and why it's never likely to be.

Taztrophe
Posts: 1
Joined: Tue Dec 22, 2015 11:30 pm

Re: Fun with Powershell And TV on Blu Ray

Post by Taztrophe » Sun Dec 27, 2015 1:10 am

Kudos on all the work. I'm running MakeMKV and Handbrake on Linux to rip my DVDs or I'd try out your work. I've been writing and reworking my own automatic BASH script and while no where NEAR as sophisticated as your Powershell script, I feel your pain.

Tonight I got my script pulling JUST the individual episodes from a TV show DVD and it kills me that I have to rescan the disc for EVERY title. Feels like since the GUI can pull multiple titles at once, MakeMKVcon should be able to do it to. Ah well. At least it's fire and forget.

Keep up the good work. Your insight helps some of us write our own solutions on other platforms. :D

ndjamena
Posts: 830
Joined: Mon Jan 07, 2013 12:23 am

Re: Fun with Powershell And TV on Blu Ray

Post by ndjamena » Sun Dec 27, 2015 1:48 am

At the moment I'm thinking if I treat the episodes not in the playlist as specials rather than as episodes and sub-number them into positions between the play-listed episodes, that may work.

At least it will get them in the right order.


This is the second part of my process

Code: Select all

@if "%EMode%"=="" Set EMode=OFF
@ECHO %EMode%

Set "ShowName=Star Trek_ The Next Generation"
Set "DiscsPerSeason="
if defined DiscsPerSeason ( Set /a DiscsPerSeason-=1 )
Set Count=0
if defined DiscsPerSeason ( Set "SeasonCount=%DiscsPerSeason%") else ( Set "SeasonCount=" )
Set SeasonNumber=7
Set MaxSeason=8

rem for /d %%g in (*) do CALL :renamedir "%%~fg"

for /r %%g in (*.mkv) do CALL :renameFile "%%~fg"

pause

goto :eof

:renamedir

	set "FolderName=%~nx1"
	Set "PartName=%FolderName:~4%"
	if "%PartName:~1%"=="" Set "PartName=0%PartName%"

	echo %PartName%

	Rename "%~f1" "Disc%PartName%"

goto :eof

:renameFile

rem	if %Count% GTR 8 (
rem		Set Count=1
rem		Set /a Count2+=1
rem	)
	if NOT "%Folder%"=="%~dp1" (
		echo Processing Folder: "%~dp1"
		if defined DiscsPerSeason (
			Set /a SeasonCount+=1
			if "%SeasonCount%"=="%DiscsPerSeason%" (
				Set "Count=0"
				Set "SeasonCount=0"
				if %SeasonNumber% LSS %MaxSeason% Set /a SeasonNumber+=1
			)
		)
	)

	Set /a Count+=1
	Set EpisodeString=%Count%
	Set SeasonString=%SeasonNumber%
	if "%EpisodeString:~1%"=="" Set "EpisodeString=0%EpisodeString%"
	if "%SeasonString:~1%"=="" Set "SeasonString=0%SeasonString%"

	Set "FileName=%ShowName% - %SeasonString%x%EpisodeString%.mkv"


	if "%Folder%%OFile%"=="%~f1" (
		Set /a Count-=1
	) else (
		echo Renaming: "%~nx1" to "%FileName%"
		REN "%~f1" "%FileName%"
	)
	if NOT "%Folder%"=="%~dp1" (
		Set "OFile=%FileName%"
		Set "Folder=%~dp1"
	)
	
goto :eof
After cleaning out all the specials and making sure the episodes are in the right order, I customise that batch script and it iterates through all the "Disc XX" folders, renaming them with season and episode numbers.

After that I point Media Companion at the directory and it retrieves metadata for each of the files, then I have a powershell script that reads the metadata files and renames the MKVs in my prefered way [ TVShow - SSxWW - Episode Name ] ( with all non-file system characters replaced with underscores so that my other scripts KNOW that there's supposed to be something else there.

My next script sets the segment title (to the file name with non-file system characters included) and all the track names (movies have to be done manually, but TV Shows are rather predictable). Then once I've decided if I can be bothered messing with the chapters I have a final script that remuxes them into their final positions using MKVMerge.

This script is making it a rather non-eventful process, and what comes out the other end is perfect and beautiful.

ndjamena
Posts: 830
Joined: Mon Jan 07, 2013 12:23 am

Re: Fun with Powershell And TV on Blu Ray

Post by ndjamena » Mon Dec 28, 2015 3:16 pm

Code: Select all

Title:0                                                                                                                             
 Duration:.....0:41:41.......................(2501)                                                                                 
  Segment.49:..2501................................................................................................................ 
 End Durations.                                                                                                                     
 Properties:                                                                                                                        
  Output.Name:........01-00801-00803-00807.mkv..................................................................................... 
  Chapter.Count:......12........................................................................................................... 
 End                                                                                                                                
Title:1                                                                                                                             
 Duration:.....0:47:28.......................(2848)                                                                                 
  Segment.50:..2848................................................................................................................ 
 End Durations.                                                                                                                     
 Properties:                                                                                                                        
  Output.Name:........02-00250-00802-00808.mkv..................................................................................... 
  Chapter.Count:......12........................................................................................................... 
 End                                                                                                                                
Title:2                                                                                                                             
 Duration:.....0:43:25.......................(2605)                                                                                 
  Segment.58:..2605................................................................................................................ 
 End Durations.                                                                                                                     
 Properties:                                                                                                                        
  Output.Name:........03-00810-00811.mkv........................................................................................... 
  Chapter.Count:......12........................................................................................................... 
 End                                                                                                                                
End Titles.                            
Doctor Who, Season 5, Disc 3.

If that didn't have a Play All, writing PowerShell code to figure out the correct order would be a PITA. Somehow I'd have to come to the conclusion to discard the number 250 before deciding on the order, because if I decide with that included 802 is before 803 and then I'd have two reasons to come to the wrong order. Without an 809, the third episode is on it's own, so can't be used to help deicide the order of the first two. I'd have to decide 00801+00802 with 00807+00808 plus the segment numbers should win out without ruling out the possibility that there could be gaps in the mpls numbers (which there are).

Thankfully there is a play all.

If I could be bothered I'd turn Play All off and try to guess the orders without it, just to improve the code.

Post Reply