mirror of
https://github.com/staxrip/staxrip.git
synced 2026-10-18 14:40:32 +08:00
389 lines
14 KiB
VB.net
389 lines
14 KiB
VB.net
Imports System.Text
|
|
Imports System.Text.RegularExpressions
|
|
Imports System.Web.UI.WebControls
|
|
|
|
Imports VB6 = Microsoft.VisualBasic
|
|
|
|
|
|
<Serializable>
|
|
Public Class RpuPreset
|
|
Public ReadOnly Property Id As Integer = 0
|
|
Public ReadOnly Property Offset As New Padding(0)
|
|
|
|
Public Sub New(id As Integer, offset As Padding)
|
|
Me.Id = id
|
|
Me.Offset = offset
|
|
End Sub
|
|
End Class
|
|
|
|
<Serializable>
|
|
Public Class RpuEdit
|
|
Public ReadOnly Property Id As Integer = 0
|
|
Public ReadOnly Property StartFrame As Integer
|
|
Public ReadOnly Property EndFrame As Integer
|
|
|
|
Public Sub New(id As Integer, startFrame As Integer, endFrame As Integer)
|
|
Me.Id = id
|
|
Me.StartFrame = startFrame
|
|
Me.EndFrame = endFrame
|
|
End Sub
|
|
End Class
|
|
|
|
<Serializable>
|
|
Public Class DolbyVisionMetadataFile
|
|
<NonSerialized> Private Shared ReadOnly _autoCropThresholdBegin As Integer = 1000
|
|
<NonSerialized> Private Shared ReadOnly _autoCropThresholdEnd As Integer = 1000
|
|
Private _crop As Padding? = Nothing
|
|
Private _lastWriteTime As DateTime = Nothing
|
|
Private _lastWriteTimeL5 As DateTime = Nothing
|
|
Private _trimRanges As List(Of Range) = Nothing
|
|
|
|
Public ReadOnly Property Path As String = Nothing
|
|
Public ReadOnly Property Edits As New List(Of RpuEdit)
|
|
Public ReadOnly Property Presets As New List(Of RpuPreset)
|
|
|
|
Public ReadOnly Property LastWriteTime As DateTime
|
|
Get
|
|
Return _lastWriteTime
|
|
End Get
|
|
End Property
|
|
|
|
Public ReadOnly Property LastWriteTimeL5 As DateTime
|
|
Get
|
|
Return _lastWriteTimeL5
|
|
End Get
|
|
End Property
|
|
|
|
|
|
Public ReadOnly Property Level5JsonFilePath As String
|
|
Get
|
|
Return If(String.IsNullOrWhiteSpace(Path), "", $"{Path.DirAndBase()}_L5.json")
|
|
End Get
|
|
End Property
|
|
|
|
Public ReadOnly Property EditorConfigFilePath As String
|
|
Get
|
|
Return If(String.IsNullOrWhiteSpace(Path), "", $"{Path.DirAndBase()}_Config.json")
|
|
End Get
|
|
End Property
|
|
|
|
Public ReadOnly Property ModifiedRpuFilePath As String
|
|
Get
|
|
Return If(String.IsNullOrWhiteSpace(Path), "", $"{Path.DirAndBase()}_Modified.rpu")
|
|
End Get
|
|
End Property
|
|
|
|
Public ReadOnly Property TrimmedRpuFilePath As String
|
|
Get
|
|
Return If(String.IsNullOrWhiteSpace(Path), "", $"{Path.DirAndBase()}_Trimmed.rpu")
|
|
End Get
|
|
End Property
|
|
|
|
Public ReadOnly Property HasPathChanged As Boolean
|
|
Get
|
|
Return Path.FileExists() AndAlso _lastWriteTime <> File.GetLastWriteTimeUtc(Path)
|
|
End Get
|
|
End Property
|
|
|
|
Public ReadOnly Property HasLevel5Changed As Boolean
|
|
Get
|
|
Return Level5JsonFilePath.FileExists() AndAlso _lastWriteTimeL5 <> File.GetLastWriteTimeUtc(Level5JsonFilePath)
|
|
End Get
|
|
End Property
|
|
|
|
Public ReadOnly Property HasToBeTrimmed As Boolean
|
|
Get
|
|
If p.Ranges Is Nothing OrElse Not p.Ranges.Any() Then Return False
|
|
If _trimRanges Is Nothing OrElse Not _trimRanges.Any() Then Return True
|
|
If p.Ranges.Count <> _trimRanges.Count Then Return True
|
|
|
|
p.Ranges.Sort()
|
|
_trimRanges.Sort()
|
|
|
|
For i = 0 To p.Ranges.Count - 1
|
|
If p.Ranges(i).CompareTo(_trimRanges(i)) <> 0 Then Return True
|
|
Next
|
|
|
|
Return False
|
|
End Get
|
|
End Property
|
|
|
|
Public ReadOnly Property Crop As Padding
|
|
Get
|
|
If _crop Is Nothing Then
|
|
Select Case p.AutoCropDolbyVisionMode
|
|
Case AutoCropDolbyVisionMode.Automatic
|
|
_crop = GetCrop(_autoCropThresholdBegin, _autoCropThresholdEnd)
|
|
Case AutoCropDolbyVisionMode.ManualThreshold
|
|
_crop = GetCrop(p.AutoCropDolbyVisionThresholdBegin, p.AutoCropDolbyVisionThresholdEnd)
|
|
Case Else
|
|
Throw New NotImplementedException(NameOf(AutoCropDolbyVisionMode))
|
|
End Select
|
|
End If
|
|
|
|
Return _crop.Value
|
|
End Get
|
|
End Property
|
|
|
|
|
|
Private Sub New()
|
|
End Sub
|
|
|
|
Public Sub New(filePath As String)
|
|
Me.Path = filePath
|
|
Me._lastWriteTime = File.GetLastWriteTimeUtc(filePath)
|
|
|
|
WriteLevel5Export(False, False)
|
|
ReadLevel5Export()
|
|
End Sub
|
|
|
|
Private Function GetCrop(thresholdBegin As Integer, thresholdEnd As Integer) As Padding
|
|
Dim newCrop As New Padding(0)
|
|
|
|
If Not Edits?.Any() Then Return newCrop
|
|
If Not Presets?.Any() Then Return newCrop
|
|
|
|
Dim max = Edits.Max(Function(x) x.EndFrame)
|
|
thresholdBegin = Math.Min(max, thresholdBegin)
|
|
thresholdBegin = Math.Max(0, thresholdBegin)
|
|
thresholdEnd = Math.Min(max, thresholdEnd)
|
|
thresholdEnd = Math.Max(0, thresholdEnd)
|
|
|
|
Dim entries = Edits.Where(Function(x) x.StartFrame < (max - thresholdEnd) AndAlso x.EndFrame > thresholdBegin) _
|
|
.Join(Presets, Function(edit) edit.Id, Function(preset) preset.Id, Function(edit, preset) New With {edit.StartFrame, edit.EndFrame, edit.Id, preset.Offset}) _
|
|
.OrderBy(Function(x) x.StartFrame).ToList()
|
|
|
|
If entries.Count < 1 Then Return newCrop
|
|
|
|
newCrop.Left = entries.Min(Function(x) x.Offset.Left)
|
|
newCrop.Top = entries.Min(Function(x) x.Offset.Top)
|
|
newCrop.Right = entries.Min(Function(x) x.Offset.Right)
|
|
newCrop.Bottom = entries.Min(Function(x) x.Offset.Bottom)
|
|
|
|
Return newCrop
|
|
End Function
|
|
|
|
Public Sub RefreshLevel5Data()
|
|
If Not HasLevel5Changed Then Exit Sub
|
|
If Not (p.AutoCropMode = AutoCropMode.DolbyVisionOnly OrElse p.AutoCropMode = AutoCropMode.Always) Then Exit Sub
|
|
|
|
ReadLevel5Export()
|
|
End Sub
|
|
|
|
Public Sub ReadLevel5Export(Optional logContent As Boolean = True)
|
|
If Not Path?.FileExists() Then Return
|
|
If Not Level5JsonFilePath.FileExists() Then Return
|
|
|
|
Edits.Clear()
|
|
Presets.Clear()
|
|
|
|
Try
|
|
Dim jsonContent = Level5JsonFilePath.ReadAllText()
|
|
Dim strippedJsonContent = jsonContent.Right("presets").Left("edits")
|
|
strippedJsonContent = Regex.Replace(strippedJsonContent, "\s", "")
|
|
Dim presetMatches = Regex.Matches(strippedJsonContent, "\{.+?:(?<id>\d+),.+?:(?<left>\d+),.+?:(?<right>\d+),.+?:(?<top>\d+),.+?:(?<bottom>\d+)\}")
|
|
|
|
If presetMatches.Count = 0 Then Return
|
|
|
|
For Each match As Match In presetMatches
|
|
Dim offset = New Padding(match.Groups("left").Value.ToInt(), match.Groups("top").Value.ToInt(), match.Groups("right").Value.ToInt(), match.Groups("bottom").Value.ToInt())
|
|
Presets.Add(New RpuPreset(match.Groups("id").Value.ToInt(), offset))
|
|
Next
|
|
|
|
strippedJsonContent = jsonContent.Right("edits")
|
|
strippedJsonContent = Regex.Replace(strippedJsonContent, "\s", "")
|
|
Dim editMatches = Regex.Matches(strippedJsonContent, """(?<start>\d+)-(?<end>\d+)"":(?<id>\d+),?")
|
|
|
|
For Each match As Match In editMatches
|
|
Edits.Add(New RpuEdit(match.Groups("id").Value.ToInt(), match.Groups("start").Value.ToInt(), match.Groups("end").Value.ToInt()))
|
|
Next
|
|
|
|
_lastWriteTimeL5 = File.GetLastWriteTimeUtc(Level5JsonFilePath)
|
|
Catch ex As AbortException
|
|
Throw ex
|
|
Catch ex As Exception
|
|
g.ShowException(ex)
|
|
Throw New AbortException
|
|
Finally
|
|
If logContent Then
|
|
Dim exists = File.Exists(Level5JsonFilePath)
|
|
Dim content = If(exists, File.ReadAllText(Level5JsonFilePath), "No file found!")
|
|
|
|
Log.WriteHeader($"Content of {IO.Path.GetFileName(Level5JsonFilePath)}")
|
|
Log.WriteLine(content)
|
|
End If
|
|
|
|
Log.Save()
|
|
End Try
|
|
End Sub
|
|
|
|
Public Sub WriteLevel5Export(Optional logContent As Boolean = False, Optional overwrite As Boolean = False)
|
|
If Not Path?.FileExists() Then Return
|
|
If Not overwrite AndAlso Level5JsonFilePath.FileExists() Then Return
|
|
|
|
Try
|
|
Dim pd = Path.Dir
|
|
Dim l5 = Level5JsonFilePath.Replace(pd, ".")
|
|
Dim arguments = $"export --data ""level5={l5}"" -i ""{Path}"""
|
|
Using proc As New Proc
|
|
proc.Package = Package.DoViTool
|
|
proc.Project = p
|
|
proc.Header = "Extract Level5 data from RPU metadata file"
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Arguments = arguments
|
|
proc.WorkingDirectory = pd
|
|
proc.AllowedExitCodes = {0}
|
|
proc.OutputFiles = {Level5JsonFilePath}
|
|
proc.Start()
|
|
End Using
|
|
Catch ex As AbortException
|
|
Throw ex
|
|
Catch ex As Exception
|
|
g.ShowException(ex)
|
|
Throw New AbortException
|
|
Finally
|
|
If logContent Then
|
|
Dim exists = File.Exists(Level5JsonFilePath)
|
|
Dim content = If(exists, File.ReadAllText(Level5JsonFilePath), "No file created!")
|
|
|
|
Log.WriteHeader($"Content of {IO.Path.GetFileName(Level5JsonFilePath)}")
|
|
Log.WriteLine(content)
|
|
End If
|
|
|
|
Log.Save()
|
|
End Try
|
|
End Sub
|
|
|
|
Public Function ReadProfileFromRpu() As Integer
|
|
If Not Path?.FileExists() Then Return -1
|
|
|
|
Dim output = ProcessHelp.GetConsoleOutput(Package.DoViTool.Path.Escape, $"info -i {Path.Escape} -s", False).Split(New String() {VB6.vbCrLf, VB6.vbLf, VB6.vbCr}, StringSplitOptions.RemoveEmptyEntries)
|
|
|
|
For i = 0 To output.Length - 1
|
|
Dim line = output(i).Trim()
|
|
Dim match = Regex.Match(line, "Profile: (\d+)(.*)$")
|
|
If match.Success Then
|
|
Dim profile = match.Groups(1).Value
|
|
Return profile.ToInt()
|
|
End If
|
|
Next
|
|
|
|
Return -1
|
|
End Function
|
|
|
|
Public Sub WriteEditorConfigFile(offset As Padding, mode As DoviMode, Optional logContent As Boolean = True, Optional overwrite As Boolean = True)
|
|
If Not Level5JsonFilePath?.FileExists() Then Return
|
|
If Not overwrite AndAlso EditorConfigFilePath?.FileExists() Then Return
|
|
|
|
Try
|
|
Dim p = ""
|
|
For Each entry In Presets.OrderBy(Function(x) x.Id)
|
|
Dim left = Math.Max(0, entry.Offset.Left - offset.Left)
|
|
Dim top = Math.Max(0, entry.Offset.Top - offset.Top)
|
|
Dim right = Math.Max(0, entry.Offset.Right - offset.Right)
|
|
Dim bottom = Math.Max(0, entry.Offset.Bottom - offset.Bottom)
|
|
|
|
p += $"{{"
|
|
p += $"""id"":{entry.Id},"
|
|
p += $"""left"":{left},"
|
|
p += $"""right"":{right},"
|
|
p += $"""top"":{top},"
|
|
p += $"""bottom"":{bottom}"
|
|
p += $"}},"
|
|
Next
|
|
p = p.TrimEnd(","c)
|
|
p = $"""presets"":[{p}]"
|
|
|
|
Dim e = ""
|
|
For Each entry In Edits.OrderBy(Function(x) x.StartFrame)
|
|
e += $"""{entry.StartFrame}-{entry.EndFrame}"":{entry.Id},"
|
|
Next
|
|
e = e.TrimEnd(","c)
|
|
e = $"""edits"":{{{e}}}"
|
|
|
|
Dim result = $"{{""mode"":{mode + 0},""active_area"":{{""crop"":false,{p},{e}}}}}"
|
|
result.WriteFileUTF8(EditorConfigFilePath)
|
|
Catch ex As AbortException
|
|
Throw ex
|
|
Catch ex As Exception
|
|
g.ShowException(ex)
|
|
Throw New AbortException
|
|
Finally
|
|
If logContent Then
|
|
Dim exists = File.Exists(EditorConfigFilePath)
|
|
Dim content = If(exists, File.ReadAllText(EditorConfigFilePath), "No file created!")
|
|
|
|
Log.WriteHeader($"Content of {IO.Path.GetFileName(EditorConfigFilePath)}")
|
|
Log.WriteLine(content)
|
|
End If
|
|
|
|
Log.Save()
|
|
End Try
|
|
End Sub
|
|
|
|
Public Function WriteModifiedRpu(Optional overwrite As Boolean = True) As String
|
|
If Not Path?.FileExists() Then Return Nothing
|
|
If Not EditorConfigFilePath?.FileExists() Then Return Nothing
|
|
If Not overwrite AndAlso ModifiedRpuFilePath.FileExists() Then Return Nothing
|
|
|
|
Try
|
|
Dim arguments = $"editor -i ""{Path}"" -j ""{EditorConfigFilePath}"" -o ""{ModifiedRpuFilePath}"""
|
|
Using proc As New Proc
|
|
proc.Package = Package.DoViTool
|
|
proc.Project = p
|
|
proc.Header = "Creating new RPU metadata file"
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Arguments = arguments
|
|
proc.AllowedExitCodes = {0}
|
|
proc.OutputFiles = {ModifiedRpuFilePath}
|
|
proc.Start()
|
|
End Using
|
|
Catch ex As AbortException
|
|
Throw ex
|
|
Catch ex As Exception
|
|
g.ShowException(ex)
|
|
Throw New AbortException
|
|
Finally
|
|
Log.Save()
|
|
End Try
|
|
|
|
Return ModifiedRpuFilePath
|
|
End Function
|
|
|
|
Public Function TrimRpu(Optional overwrite As Boolean = True) As String
|
|
If Not Path?.FileExists() Then Return Nothing
|
|
If Not HasToBeTrimmed Then Return Nothing
|
|
If Not overwrite AndAlso TrimmedRpuFilePath.FileExists() Then Return Nothing
|
|
|
|
Dim inputFilePath = If(ModifiedRpuFilePath.FileExists(), ModifiedRpuFilePath, Path)
|
|
_trimRanges = New List(Of Range)(p.Ranges)
|
|
|
|
Try
|
|
Dim frames = Edits.Max(Function(x) x.EndFrame) + 1
|
|
Dim ranges = String.Join(";", _trimRanges.Select(Function(x) $"{x.Start}-{x.End}"))
|
|
Dim arguments = $"--input ""{inputFilePath}"" --output ""{TrimmedRpuFilePath}"" --frames {frames} --ranges {ranges}"
|
|
Using proc As New Proc
|
|
proc.Package = Package.DoViSplit
|
|
proc.Project = p
|
|
proc.Header = "Trimming RPU metadata file"
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Arguments = arguments
|
|
proc.AllowedExitCodes = {0}
|
|
proc.OutputFiles = {TrimmedRpuFilePath}
|
|
proc.Start()
|
|
End Using
|
|
Catch ex As AbortException
|
|
_trimRanges = Nothing
|
|
Throw ex
|
|
Catch ex As Exception
|
|
_trimRanges = Nothing
|
|
g.ShowException(ex)
|
|
Throw New AbortException
|
|
Finally
|
|
Log.Save()
|
|
End Try
|
|
|
|
Return TrimmedRpuFilePath
|
|
End Function
|
|
End Class
|