Files
staxrip/Source/Forms/MainForm_Assistant.vb
2026-02-16 04:41:07 +01:00

487 lines
26 KiB
VB.net

Imports Microsoft.VisualBasic
Imports StaxRip.UI
Partial Public Class MainForm
Inherits FormBase
Function Assistant(Optional refreshScript As Boolean = True) As Boolean
If SkipAssistant Then Return False
If ThemeRefresh Then
ApplyTheme()
ThemeRefresh = False
End If
Try
SkipAssistant = True
If refreshScript Then
p.Script.Synchronize(False, False)
ElseIf p.Script.Info.FrameCount = 0 Then
Using server = FrameServerFactory.Create(p.Script.Path)
p.Script.Info = server.Info
p.Script.Error = server.Error
End Using
End If
g.CheckForModifiedDolbyVisionLevel5Data()
Dim isCropped = p.Script.IsFilterActive("Crop")
Dim isResized = p.Script.IsFilterActive("Resize")
tbTargetWidth.ReadOnly = Not isResized
tbTargetHeight.ReadOnly = Not isResized
Dim cropw = p.SourceWidth
Dim croph = p.SourceHeight
If isCropped Then
cropw = p.SourceWidth - p.CropLeft - p.CropRight
croph = p.SourceHeight - p.CropTop - p.CropBottom
End If
Dim isValidAnamorphicSize = (p.TargetWidth = 1440 AndAlso p.TargetHeight = 1080) OrElse
(p.TargetWidth = 960 AndAlso p.TargetHeight = 720)
If Not isResized Then
If p.TargetWidth <> cropw Then
tbTargetWidth.Text = cropw.ToString
End If
If p.TargetHeight <> croph Then
tbTargetHeight.Text = croph.ToString
End If
End If
lAspectRatioError.Text = Calc.GetAspectRatioError.ToInvariantString("f2") + "%"
lCrop.Text = If(isCropped, $"{cropw}/{croph}", "disabled")
lCrop2.Text = If(isCropped, $"{p.CropLeft}, {p.CropRight} / {p.CropTop}, {p.CropBottom}", "-")
Dim widthZoom = p.TargetWidth / cropw * 100
Dim heightZoom = p.TargetHeight / croph * 100
lZoom.Text = widthZoom.ToInvariantString("f1") + "/" + heightZoom.ToInvariantString("f1")
lPixel.Text = CInt(p.TargetWidth * p.TargetHeight).ToString
SetSlider()
Dim trackBarValue = CInt(p.TargetWidth \ Math.Max(TrackBarInterval, p.ForcedOutputMod) - (tbResize.Maximum \ 3))
trackBarValue = Math.Min(trackBarValue, tbResize.Maximum)
trackBarValue = Math.Max(trackBarValue, tbResize.Minimum)
tbResize.Value = trackBarValue
Dim par = Calc.GetTargetPAR
lPAR.Text = If(Calc.IsARSignalingRequired OrElse (par.X = 1 AndAlso par.Y = 1), par.X & ":" & par.Y, "n/a")
lDAR.Text = Calc.GetTargetDAR.ToInvariantString.Shorten(8)
lSAR.Text = (p.TargetWidth / p.TargetHeight).ToInvariantString.Shorten(8)
lSourceDar.Text = Calc.GetSourceDAR.ToInvariantString.Shorten(8)
par = Calc.GetSimpleSourcePAR
lSourcePAR.Text = par.X & ":" & par.Y
If p.SourceSeconds > 0 Then
Dim size = If(p.SourceVideoSize > 0, p.SourceVideoSize, p.SourceSize)
Dim sizeText = If(size / PrefixedSize(2).Factor < SizePrefix.Base, CInt(size / PrefixedSize(2).Factor).ToString + PrefixedSize(2).Unit, (size / PrefixedSize(3).Factor).ToString("f1") + PrefixedSize(3).Unit)
If size <> p.SourceVideoSize Then
sizeText = $"[{sizeText}]"
End If
lSource1.Text = lSource1.GetMaxTextSpace(
g.GetTimeString(p.SourceSeconds),
sizeText,
If(p.SourceBitrate > 0, (p.SourceBitrate / 1000).ToInvariantString("f1") + "Mb/s", ""),
p.SourceFrameRate.ToInvariantString.Shorten(9) + "fps",
p.SourceFrameRateMode)
lSource2.Text = lSource1.GetMaxTextSpace(
p.SourceWidth.ToString + "x" + p.SourceHeight.ToString, p.SourceColorSpace,
p.SourceChromaSubsampling, If(p.SourceVideoBitDepth <> 0, p.SourceVideoBitDepth & "Bits", ""),
p.SourceVideoHdrFormat,
p.SourceScanType, If(p.SourceScanType.EqualsAny("Interlaced", "MBAFF"), p.SourceScanOrder, ""),
p.SourceVideoFormat, p.SourceVideoFormatProfile)
lTarget1.Text = lSource1.GetMaxTextSpace(g.GetTimeString(p.TargetSeconds),
p.TargetFrameRate.ToInvariantString.Shorten(9) + "fps", p.Script.Info.Width & "x" & p.Script.Info.Height,
"Audio Bitrate: " & CInt(Calc.GetAudioBitrate))
If p.VideoEncoder.IsCompCheckEnabled Then
laTarget2.Text = lSource1.GetMaxTextSpace(
"Quality: " & CInt(Calc.GetPercent).ToInvariantString() + " %",
"Compressibility: " + p.Compressibility.ToInvariantString("f2"))
Else
Dim subtitles = p.VideoEncoder.Muxer.Subtitles.Where(Function(i) i.Enabled)
laTarget2.Text = "Subtitles: " & subtitles.Count & " " + subtitles.Select(Function(i) i.TypeName).Distinct.Join("/")
End If
Else
lTarget1.Text = ""
lSource1.Text = ""
laTarget2.Text = ""
lSource2.Text = ""
End If
AssistantClickAction = Nothing
CanIgnoreTip = True
AssistantPassed = False
If p.VideoEncoder.Muxer.TagFile <> "" AndAlso File.Exists(p.VideoEncoder.Muxer.TagFile) AndAlso
p.VideoEncoder.Muxer.Tags.Count > 0 Then
If ProcessTip("In the container options there is both a tag file and tags in the Tags tab defined. Only one can be used, the file will be ignored.") Then
Return Warn("Tags are defined twice")
End If
End If
If p.VideoEncoder.Muxer.CoverFile <> "" AndAlso TypeOf p.VideoEncoder.Muxer Is MkvMuxer Then
If Not p.VideoEncoder.Muxer.CoverFile.Base.EqualsAny("cover", "small_cover", "cover_land", "small_cover_land") OrElse
Not p.VideoEncoder.Muxer.CoverFile.Ext.EqualsAny("jpg", "png") Then
If ProcessTip("The cover file name bust be cover, small_cover, cover_land or small_cover_land, the file type must be jpg or png.") Then
Return Block("Invalid Cover File Name")
End If
End If
End If
Dim enc = TryCast(p.VideoEncoder, BasicVideoEncoder)
If enc IsNot Nothing Then
Dim param = enc.CommandLineParams.GetOptionParam("--vpp-resize")
If param IsNot Nothing AndAlso param.Value > 0 AndAlso Not p.Script.IsFilterActive("Resize", "Hardware Encoder") Then
If ProcessTip("In order to use a resize filter of the hardware encoder select 'Hardware Encoder' as resize filter from the filters menu.") Then
Return Block("Invalid Filter Setting")
End If
End If
End If
If Not p.BatchMode Then
If p.SourceFile <> "" AndAlso p.Script.IsAviSynth AndAlso Not TextEncoding.ArePathsSupportedByProcessEncoding AndAlso
ProcessTip("AviSynth Unicode support requires at least Windows 10 1903.") Then
Return Block("Text Encoding Limitation", lgbEncoder.Label)
End If
If p.Script.Filters.Count = 0 OrElse Not p.Script.Filters(0).Active OrElse
p.Script.Filters(0).Category <> "Source" Then
If ProcessTip("The first filter must have the category Source.") Then
Return Block("Invalid Filter Setup")
End If
End If
If p.SourceSeconds = 0 AndAlso ProcessTip("Click here to open a source file.") Then
CanIgnoreTip = False
Return Warn("Assistant", AddressOf ShowOpenSourceDialog)
End If
If p.SourceFile = p.TargetFile AndAlso ProcessTip("The source and target filepath is identical.") Then
Return Block("Invalid Target Path", tbSourceFile, tbTargetFile)
End If
If p.RemindToCrop AndAlso TypeOf p.VideoEncoder IsNot NullEncoder AndAlso
ProcessTip("Click here to open the crop dialog. When done continue with Next.") Then
Return Warn("Crop", AddressOf ShowCropDialog)
End If
If p.WarnNoAudio Then
If Not p.AudioTracks.Any(Function(track) track.IsRelevant) Then
If ProcessTip("There will be no audio in the output file.") Then
Return Warn("No audio", p.AudioTracks.Select(Function(x) x.TextEdit).ToArray())
End If
End If
End If
If p.WarnIdenticalAudio Then
Dim fileGroups = p.AudioTracks.Where(Function(x) x.TextEdit.Text <> "" AndAlso x.AudioProfile.Stream Is Nothing).GroupBy(Function(g) g.AudioProfile.File).Where(Function(x) x.Count() > 1).ToList()
If fileGroups.Any() Then
If ProcessTip($"Some audio source files are identical.") Then
Return Warn("Suspicious Audio Settings", fileGroups.SelectMany(Function(x) x.AsEnumerable().Select(Function(s) s.TextEdit)).ToArray())
End If
End If
Dim streamGroups = p.AudioTracks.Where(Function(x) x.AudioProfile.Stream IsNot Nothing).GroupBy(Function(g) g.AudioProfile.Stream.Index).Where(Function(x) x.Count() > 1).ToList()
If streamGroups.Any() Then
If ProcessTip($"Some audio source streams are identical.") Then
Return Warn("Suspicious Audio Settings", streamGroups.SelectMany(Function(x) x.AsEnumerable().Select(Function(s) s.TextEdit)).ToArray())
End If
End If
End If
If Not p.VideoEncoder.Muxer.IsSupported(p.VideoEncoder.OutputExt) Then
If ProcessTip("The encoder outputs '" + p.VideoEncoder.OutputExt + "' but the container '" + p.VideoEncoder.Muxer.Name + "' supports only " + p.VideoEncoder.Muxer.SupportedInputTypes.Join(", ") + ".") Then
Return Block("Encoder conflicts with container", lgbEncoder.Label, llMuxer)
End If
End If
For Each ap In AudioProfile.GetProfiles
If ap.File = "" Then Continue For
If TypeOf ap Is GUIAudioProfile Then
Dim gap = DirectCast(ap, GUIAudioProfile)
If (gap.AudioCodec = AudioCodec.AC3 OrElse gap.AudioCodec = AudioCodec.EAC3) AndAlso
(gap.Channels = 7 OrElse gap.Channels = 8) AndAlso gap.GetEncoder = GuiAudioEncoder.ffmpeg Then
If ProcessTip("AC3/EAC3 6.1/7.1 is not supported by ffmpeg.") Then
Return Block("Invalid Audio Channel Count", GetAudioTextBox(ap))
End If
End If
End If
If ap.AudioCodec = AudioCodec.AC3 AndAlso CInt(ap.Bitrate) Mod If(CInt(ap.Bitrate) > 256, 64, 32) <> 0 Then
If ProcessTip($"The AC3 bitrate {CInt(ap.Bitrate)} is not specification compliant.") Then
Return Warn("Invalid Audio Bitrate", GetAudioTextBox(ap))
End If
End If
If ap.File = p.TargetFile AndAlso ProcessTip("The audio source and target filepath is identical.") Then
Return Block("Invalid Targetpath", GetAudioTextBox(ap), tbTargetFile)
End If
If Math.Abs(ap.Delay) > 2000 AndAlso ProcessTip("The audio delay is unusual high indicating a sync problem.") Then
Return Warn("High Audio Delay", GetAudioTextBox(ap))
End If
If Not p.VideoEncoder.Muxer.IsSupported(ap.OutputFileType) AndAlso Not ap.OutputFileType = "ignore" Then
If ProcessTip("The audio format is '" + ap.OutputFileType + "' but the container '" +
p.VideoEncoder.Muxer.Name + "' supports only " +
p.VideoEncoder.Muxer.SupportedInputTypes.Join(", ") +
". Select another audio profile or another container.") Then
Return Block("Audio format not compatible with container", GetAudioTextBox(ap), llMuxer)
End If
End If
Next
If p.VideoEncoder.Muxer.OutputExtFull <> p.TargetFile.ExtFull Then
If ProcessTip("The container requires " + p.VideoEncoder.Muxer.OutputExt.ToUpperInvariant + " as target file type.") Then
Return Block("Invalid File Type", tbTargetFile)
End If
End If
If p.VideoEncoder.GetError IsNot Nothing Then
If ProcessTip(p.VideoEncoder.GetError) Then
Return Block("Encoder Error")
End If
End If
Dim ae = Calc.GetAspectRatioError()
If Not isValidAnamorphicSize AndAlso (ae > p.MaxAspectRatioError OrElse ae < -p.MaxAspectRatioError) AndAlso
isResized AndAlso p.WarnArError AndAlso p.CustomTargetPAR <> "1:1" Then
If ProcessTip("Use the resize slider to correct the aspect ratio error or click Next to encode anamorphic.") Then
Return Warn("Aspect Ratio Error", lAspectRatioError)
End If
End If
If p.RemindToSetFilters AndAlso ProcessTip("Verify the filter setup, when done continue with Next.") Then
Return Warn("Filter Setup")
End If
If p.Ranges.Count = 0 Then
If p.RemindToCut AndAlso TypeOf p.VideoEncoder IsNot NullEncoder AndAlso
ProcessTip("Click here to open the preview for cutting if necessary. When done continue with Next.") Then
Return Warn("Cutting", AddressOf ShowPreview)
End If
Else
If p.CutFrameRate <> p.Script.Info.FrameRate Then
If ProcessTip("The frame rate was changed after cutting was performed, please ensure that this change is happening after the Cutting filter section in the AviSynth script.") Then
Return Warn("Frame Rate Change")
End If
End If
If Not p.Script.IsFilterActive("Cutting") AndAlso Form.ActiveForm Is Me Then
If ProcessTip("The cutting filter settings don't match with the cutting settings used in the preview." + BR +
"This can usually be fixed by opening and closing the preview.") Then
Return Block("Invalid Cutting Settings")
End If
End If
End If
If p.RemindToDoCompCheck AndAlso p.VideoEncoder.IsCompCheckEnabled AndAlso p.Compressibility = 0 Then
If ProcessTip("Click here to start the compressibility check. The compressibility check helps to finds the ideal bitrate or image size.") Then
Return Warn("Compressibility Check", AddressOf p.VideoEncoder.RunCompCheck, laTarget2)
End If
End If
If Not p.TargetFile.IsValidPath() Then
If ProcessTip("The target file path is invalid." + BR + p.TargetFile) Then
Return Warn("Invalid Target File", tbTargetFile)
End If
End If
If File.Exists(p.TargetFile) Then
If FileTypes.VideoText.Contains(p.SourceFile.Ext) AndAlso p.SourceFile.ReadAllText.Contains(p.TargetFile) Then
If ProcessTip("Source and target name are identical, please select another target name.") Then
Return Block("Invalid Target File", tbTargetFile)
End If
Else
If ProcessTip("The target file already exists." + BR + p.TargetFile) Then
Return Warn("Target File", tbTargetFile)
End If
End If
End If
If TypeOf p.VideoEncoder.Muxer Is MkvMuxer AndAlso Not String.IsNullOrWhiteSpace(p.VideoEncoder.Muxer.TimestampsFile) Then
Dim sfc = p.SourceScript.GetFrameCount()
Dim tfc = p.Script.GetFrameCount()
If sfc <> tfc Then
If ProcessTip("The duration changed and doesn't fit the original timestamps, which can cause unwanted results. You can delete/change the timestamps file selection under Container Options > Options.") Then
Return Warn("Changed Length", lSource1, lTarget1)
End If
End If
Dim sfr = p.SourceScript.GetFramerate()
Dim tfr = p.Script.GetFramerate()
If sfr <> tfr Then
If ProcessTip("The frame rate changed and could not fit the original timestamps anymore, which can cause unwanted results. You can delete/change the timestamps file selection under Container Options > Options.") Then
Return Warn("Changed Frame Rate", lSource1, lTarget1)
End If
End If
End If
If p.Script.IsFilterActive("Crop") AndAlso p.VideoEncoder?.IsDolbyVisionSet Then
If p.HdrDolbyVisionMetadataFile Is Nothing Then
If ProcessTip($"You have set a Dolby Vision metadata file, that is unknown to StaxRip, while cropping the video. Please make sure it is extracted with the source file.") Then
Return Block("Unknown RPU File", AddressOf p.VideoEncoder.ShowConfigDialog)
End If
End If
End If
If p.Script.IsFilterActive("Resize") AndAlso widthZoom <> heightZoom AndAlso p.VideoEncoder?.IsResizingAllowed AndAlso Not p.VideoEncoder?.IsUnequalResizingAllowed Then
Dim arw = p.TargetWidth / cropw
Dim h = croph * arw
Dim res = CInt(If(CInt(Fix(h)) Mod 2 = 0, Math.Floor(h), Math.Floor(h + 1)))
If p.TargetHeight <> res Then
If ProcessTip("Resizing of that kind will interfere with the Dolby Vision metadata. Keep the original aspect ratio, disable the 'Resize' filter or remove the Dolby Vision RPU file.") Then
Return Block("Wrong resizing", tbTargetWidth, tbTargetHeight, lSAR, lDAR, lZoom, lAspectRatioError)
End If
End If
End If
If TypeOf p.VideoEncoder Is x265Enc Then
Dim x265 = DirectCast(p.VideoEncoder, x265Enc)
Dim rpuParam = x265.Params.DolbyVisionRpu
Dim bufsizeParam = x265.Params.VbvBufSize
Dim maxrateParam = x265.Params.VbvMaxRate
Dim optionsLabel = DirectCast(pnEncoder.Controls(0), x265Control).blConfigCodec
If Not String.IsNullOrWhiteSpace(rpuParam.Value) Then
If bufsizeParam?.Value < 1 Then
If ProcessTip("Dolby Vision requires VBV settings to enable HRD.") Then
Return Block("Missing VBV settings", Sub() x265.ShowConfigDialog(bufsizeParam), optionsLabel)
End If
ElseIf maxrateParam?.Value < 1 Then
If ProcessTip("Dolby Vision requires VBV settings to enable HRD.") Then
Return Block("Missing VBV settings", Sub() x265.ShowConfigDialog(maxrateParam), optionsLabel)
End If
End If
End If
End If
If p.Script.IsAviSynth AndAlso TypeOf p.VideoEncoder Is x264Enc AndAlso
Not Package.x264.Version.ToLowerInvariant.ContainsAny("amod", "djatom", "patman") AndAlso
p.Script.Info.ColorSpace <> ColorSpace.YUV420P8 AndAlso
p.Script.Info.ColorSpace <> ColorSpace.YUV420P8_ AndAlso
p.Script.Info.ColorSpace <> ColorSpace.YUV422P8 AndAlso
p.Script.Info.ColorSpace <> ColorSpace.YUV444P8 AndAlso
p.Script.Info.ColorSpace <> ColorSpace.BGR32 AndAlso
Not g.ContainsPipeTool(p.VideoEncoder.GetCommandLine(True, True)) Then
If ProcessTip("x264 AviSynth input supports only YUV420P8, YUV422P8, YUV444P8 and BGR32 " +
$"as input colorspace.{BR}Consider to use a pipe tool: " +
"x264 Options > Input/Output > Pipe > avs2pipemod y4m") Then
Return Block("Incompatible colorspace")
End If
End If
If p.Script.Info.Width Mod p.ForcedOutputMod <> 0 AndAlso (Not p.ForcedOutputModOnlyIfCropped OrElse p.Script.Info.Width <> p.SourceScript.Info.Width) Then
Dim tip = "Change output width to be divisible by " & p.ForcedOutputMod & " or customize:" + BR + "Options > Image > Output Mod"
If Not p.ForcedOutputModIgnorable Then RemoveTip(tip)
If ProcessTip(tip) Then
CanIgnoreTip = p.ForcedOutputModIgnorable
Return Warn("Invalid Target Width", tbTargetWidth, lSAR)
End If
End If
If p.Script.Info.Height Mod p.ForcedOutputMod <> 0 AndAlso (Not p.ForcedOutputModOnlyIfCropped OrElse p.Script.Info.Height <> p.SourceScript.Info.Height) Then
Dim tip = "Change output height to be divisible by " & p.ForcedOutputMod & " or customize:" + BR + "Options > Image > Output Mod"
If Not p.ForcedOutputModIgnorable Then RemoveTip(tip)
If ProcessTip(tip) Then
CanIgnoreTip = p.ForcedOutputModIgnorable
Return Warn("Invalid Target Height", tbTargetHeight, lSAR)
End If
End If
If p.VideoEncoder.IsCompCheckEnabled AndAlso p.Compressibility > 0 Then
Dim value = Calc.GetPercent
If value < (p.VideoEncoder.AutoCompCheckValue - 20) OrElse
value > (p.VideoEncoder.AutoCompCheckValue + 20) Then
If ProcessTip("Aimed quality value is more than 20% off, change the image or file size to get something between 50% and 70% quality.") Then
Return Warn("Quality", tbTargetSize, tbBitrate, tbTargetWidth, tbTargetHeight, laTarget2)
End If
End If
End If
If TypeOf p.VideoEncoder.Muxer Is MP4Muxer Then
For Each i In p.VideoEncoder.Muxer.Subtitles
If Not i.Path.Ext.EqualsAny("idx", "srt", "sub") Then
If ProcessTip("MP4 supports only SUB, SRT and IDX subtitles.") Then
Return Block("Invalid subtitle format")
End If
End If
Next
End If
If Not (MouseButtons = MouseButtons.Left AndAlso ActiveControl Is tbResize) Then
Dim err = p.Script.Error
If err <> "" AndAlso ProcessTip(err) Then
Return Block("Click on the error message", Sub() MsgError("Script Error", err))
End If
End If
If TypeOf p.VideoEncoder Is SvtAv1EssentialEnc Then
If p.Script.Info.BitDepth <> 10 Then
If ProcessTip("This encoder requires 10-bit sources. Add a filter that converts it to 10-bit.") Then
Return Block("Invalid bit-depth")
End If
End If
End If
Else
If p.SourceFiles.Count = 0 Then
If ProcessTip("Click here to open a source file.") Then
CanIgnoreTip = False
Return Warn("Assistant", AddressOf ShowOpenSourceDialog)
End If
End If
End If
'flicker issue
If gbAssistant.Text <> "Add Job" Then
gbAssistant.Text = "Add Job"
End If
If laTip.Font.Size <> 9 Then
laTip.SetFontSize(9)
End If
laTip.Text = "Click on the button to the right to add a job to the job list."
AssistantPassed = True
bnNext.Enabled = True
laTip.BackColor = ThemeManager.CurrentTheme.MainForm.laTipBackColor
laTip.ForeColor = ThemeManager.CurrentTheme.MainForm.laTipForeColor
UpdateNextButton()
Catch ex As Exception
Finally
SkipAssistant = False
End Try
End Function
End Class