mirror of
https://github.com/staxrip/staxrip.git
synced 2026-10-25 18:51:23 +08:00
1652 lines
57 KiB
VB.net
1652 lines
57 KiB
VB.net
|
|
Imports System.Collections.ObjectModel
|
|
Imports System.Drawing.Imaging
|
|
Imports System.Globalization
|
|
Imports System.Management.Automation
|
|
Imports System.Runtime.ExceptionServices
|
|
Imports System.Security.Principal
|
|
Imports System.Text
|
|
Imports System.Text.RegularExpressions
|
|
Imports System.Threading
|
|
Imports System.Threading.Tasks
|
|
Imports System.Windows.Forms.VisualStyles
|
|
Imports Microsoft.VisualBasic.FileIO
|
|
Imports Microsoft.Win32
|
|
Imports StaxRip.UI
|
|
Imports VB6 = Microsoft.VisualBasic
|
|
|
|
Public Class GlobalClass
|
|
Property DefaultCommands As New GlobalCommands
|
|
Property DPI As Integer
|
|
Property IsAdmin As Boolean = New WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator)
|
|
Property IsJobProcessing As Boolean
|
|
Property MainForm As MainForm
|
|
Property MAX_PATH As Integer = 260
|
|
Property MinimizedWindows As Boolean
|
|
Property ProcForm As ProcessingForm
|
|
Property ProjectPath As String
|
|
Property SavedProject As New Project
|
|
Property StopAfterCurrentJob As Boolean
|
|
Property ActiveForm As Form
|
|
|
|
Event AfterJobFailed()
|
|
Event AfterJobMuxed()
|
|
Event AfterJobProcessed()
|
|
Event AfterJobsProcessed()
|
|
Event AfterProjectLoaded()
|
|
Event AfterProjectOrSourceLoaded()
|
|
Event AfterSourceLoaded()
|
|
Event AfterVideoEncoded()
|
|
Event ApplicationExit()
|
|
Event BeforeJobProcessed()
|
|
Event BeforeProcessing()
|
|
|
|
Sub WriteDebugLog(value As String)
|
|
If s?.WriteDebugLog Then
|
|
Trace.TraceInformation(value)
|
|
End If
|
|
End Sub
|
|
|
|
Function InvokePowerShellCode(code As String, Optional args As String() = Nothing) As Collection(Of PSObject)
|
|
Return InvokePowerShellCode(code, Nothing, Nothing, args)
|
|
End Function
|
|
|
|
Function InvokePowerShellCode(
|
|
code As String,
|
|
variableName As String,
|
|
variableValue As Object) As Collection(Of PSObject)
|
|
|
|
InvokePowerShellCode(code, variableName, variableValue, Nothing)
|
|
End Function
|
|
|
|
Function InvokePowerShellCode(
|
|
code As String,
|
|
variableName As String,
|
|
variableValue As Object,
|
|
args As String()) As Collection(Of PSObject)
|
|
|
|
Try
|
|
Return PowerShell.Invoke(code, variableName, variableValue, args)
|
|
Catch ex As RuntimeException
|
|
g.ShowException(ex, "PowerShell Scipt Exception",
|
|
ex.ErrorRecord.ScriptStackTrace.Replace(" <ScriptBlock>, <No file>", ""))
|
|
Catch ex As Exception
|
|
ShowException(ex)
|
|
End Try
|
|
End Function
|
|
|
|
Sub LoadPowerShellScripts()
|
|
For Each dirPath In {Folder.Apps + "Scripts", Folder.Scripts + "Auto Load"}
|
|
If dirPath.DirExists Then
|
|
For Each fp In Directory.GetFiles(dirPath, "*.ps1")
|
|
g.DefaultCommands.ExecutePowerShellFile(fp)
|
|
Next
|
|
End If
|
|
Next
|
|
End Sub
|
|
|
|
Function ConvertToCSV(delimiter As String, objects As IEnumerable(Of Object)) As String
|
|
Dim code = $"$inputVar | ConvertTo-Csv -Delimiter '{delimiter}' -NoTypeInformation"
|
|
Return "sep=" + delimiter + BR + PowerShell.InvokeAndConvert(code, "inputVar", objects)
|
|
End Function
|
|
|
|
Function IsWindowsTerminalAvailable() As Boolean
|
|
Return File.Exists(Folder.AppDataLocal + "Microsoft\WindowsApps\wt.exe")
|
|
End Function
|
|
|
|
Sub RunCodeInTerminal(code As String)
|
|
Dim base64 = Convert.ToBase64String(Encoding.Unicode.GetBytes(code)) 'UTF16LE
|
|
|
|
If s.PreferWindowsTerminal AndAlso g.IsWindowsTerminalAvailable Then
|
|
RunCommandInTerminal("wt.exe", $"powershell.exe -NoLogo -NoExit -NoProfile -EncodedCommand ""{base64}""")
|
|
Else
|
|
RunCommandInTerminal("powershell.exe", $"-NoLogo -NoExit -NoProfile -EncodedCommand ""{base64}""")
|
|
End If
|
|
End Sub
|
|
|
|
Sub RunCommandInTerminal(fileName As String, Optional arguments As String = Nothing)
|
|
Using pr As New Process
|
|
pr.StartInfo.FileName = fileName
|
|
pr.StartInfo.Arguments = arguments
|
|
pr.StartInfo.UseShellExecute = False
|
|
pr.StartInfo.WorkingDirectory = Folder.Desktop
|
|
Proc.SetEnvironmentVariables(pr)
|
|
pr.Start()
|
|
End Using
|
|
End Sub
|
|
|
|
Sub Execute(fileName As String, Optional arguments As String = Nothing)
|
|
Dim info As New ProcessStartInfo
|
|
info.UseShellExecute = False
|
|
info.FileName = fileName
|
|
|
|
If arguments <> "" Then
|
|
info.Arguments = arguments
|
|
End If
|
|
|
|
Using pr As New Process
|
|
pr.StartInfo = info
|
|
Proc.SetEnvironmentVariables(pr)
|
|
pr.Start()
|
|
End Using
|
|
End Sub
|
|
|
|
Sub ShellExecute(fileName As String, Optional arguments As String = Nothing)
|
|
Try
|
|
If Not fileName.StartsWith("http") AndAlso fileName.Ext.EqualsAny("htm", "html") Then
|
|
Dim browser = g.GetAppPathForExtension("htm", "html")
|
|
|
|
If Not browser.FileName.EqualsAny("chrome.exe", "firefox.exe") Then
|
|
browser = "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
|
|
End If
|
|
|
|
If Not browser.FileExists Then
|
|
browser = Package.FindEverywhere({"chrome.exe", "firefox.exe"})
|
|
End If
|
|
|
|
If browser.FileExists Then
|
|
arguments = fileName.Escape
|
|
fileName = browser
|
|
End If
|
|
End If
|
|
|
|
Process.Start(fileName, arguments)?.Dispose()
|
|
Catch ex As Exception
|
|
g.ShowException(ex, "Failed to start process", "Filename:" + BR2 + fileName + BR2 + "Arguments:" + BR2 + arguments)
|
|
End Try
|
|
End Sub
|
|
|
|
Sub SelectFileWithExplorer(filepath As String)
|
|
g.ShellExecute("explorer.exe", "/n, /select, " + filepath.Escape)
|
|
End Sub
|
|
|
|
Sub AddToPath(ParamArray dirs As String())
|
|
Dim path = Environment.GetEnvironmentVariable("path")
|
|
Dim newPath = path
|
|
|
|
For Each d In dirs
|
|
If d.DirExists AndAlso Not d.StartsWith(Folder.System) AndAlso Not path.Contains(d + ";") Then
|
|
newPath = d + ";" + newPath
|
|
End If
|
|
Next
|
|
|
|
If newPath <> path Then
|
|
Environment.SetEnvironmentVariable("path", newPath)
|
|
End If
|
|
End Sub
|
|
|
|
Function IsFixedDrive(path As String) As Boolean
|
|
Try
|
|
If path <> "" Then
|
|
Return New DriveInfo(path).DriveType = DriveType.Fixed
|
|
End If
|
|
Catch
|
|
End Try
|
|
End Function
|
|
|
|
Sub ProcessJobs()
|
|
For Each form In PreviewForm.Instances.ToArray
|
|
form.Close()
|
|
Next
|
|
|
|
Dim tempProject = ObjectHelp.GetCopy(p)
|
|
Dim projectPath = g.ProjectPath
|
|
Dim mainFormText = g.MainForm.Text
|
|
Dim targetFile = p.TargetFile
|
|
|
|
FrameServerHelp.AviSynthToolPath()
|
|
g.StopAfterCurrentJob = False
|
|
ProcessJobsRecursive()
|
|
|
|
Dim restore = String.IsNullOrWhiteSpace(tempProject.SourceFile) OrElse (File.Exists(tempProject.SourceFile) AndAlso Directory.Exists(tempProject.TempDir) AndAlso File.Exists(projectPath))
|
|
If restore Then
|
|
g.MainForm.OpenProject(tempProject, projectPath)
|
|
g.MainForm.tbTargetFile.Text = targetFile
|
|
g.MainForm.Text = mainFormText
|
|
g.ProjectPath = projectPath
|
|
g.UpdateTrim(p.Script)
|
|
g.MainForm.UpdateFilters()
|
|
Else
|
|
g.MainForm.OpenProject("", False)
|
|
End If
|
|
End Sub
|
|
|
|
Sub ProcessJobsRecursive()
|
|
Dim jobs = JobManager.ActiveJobs
|
|
|
|
If jobs.Count = 0 Then
|
|
Exit Sub
|
|
End If
|
|
|
|
g.IsJobProcessing = True
|
|
Dim jobPath = jobs(0).Path
|
|
|
|
Try
|
|
JobManager.ActivateJob(jobPath, False)
|
|
g.MainForm.OpenProject(jobPath, False)
|
|
|
|
If s.PreventStandby Then
|
|
PowerRequest.SuppressStandby()
|
|
End If
|
|
|
|
ProcessJob(jobPath)
|
|
jobs = JobManager.GetJobs
|
|
|
|
If jobs.Count = 0 Then
|
|
g.RaiseAppEvent(ApplicationEvent.AfterJobsProcessed)
|
|
g.ShutdownPC()
|
|
ElseIf JobManager.ActiveJobs.Count = 0 OrElse g.StopAfterCurrentJob Then
|
|
g.RaiseAppEvent(ApplicationEvent.AfterJobsProcessed)
|
|
|
|
If Process.GetProcessesByName("StaxRip").Count = 1 Then
|
|
g.ShutdownPC()
|
|
End If
|
|
Else
|
|
If Process.GetCurrentProcess.PrivateMemorySize64 / 1024 ^ 2 > 1500 Then
|
|
g.ShellExecute(Application.ExecutablePath, "-StartJobs")
|
|
g.MainForm.SetSavedProject()
|
|
g.MainForm.Close()
|
|
Else
|
|
ProcessJobsRecursive()
|
|
End If
|
|
End If
|
|
Catch ex As AbortException
|
|
Log.Save()
|
|
g.MainForm.OpenProject(g.ProjectPath, False)
|
|
Catch ex As Exception
|
|
Log.Save()
|
|
g.OnException(ex)
|
|
Finally
|
|
If s.PreventStandby Then
|
|
PowerRequest.EnableStandby()
|
|
End If
|
|
|
|
g.IsJobProcessing = False
|
|
ThemeManager.SetCurrentTheme(s.ThemeName)
|
|
g.MainForm.OpenProject(jobPath, False)
|
|
ProcController.Finished()
|
|
End Try
|
|
End Sub
|
|
|
|
Sub ProcessJob(jobPath As String)
|
|
Try
|
|
If Not File.Exists(jobPath) Then
|
|
g.RunTask(Sub() MsgError("Project file not found!", $"'{jobPath}'{BR}could not be found and got skipped!"))
|
|
Exit Sub
|
|
End If
|
|
|
|
g.RaiseAppEvent(ApplicationEvent.BeforeJobProcessed)
|
|
|
|
Dim startTime = DateTime.Now
|
|
|
|
If p.BatchMode Then
|
|
Dim missingFiles = p.SourceFiles.Where(Function(srcFile) Not srcFile.FileExists)
|
|
|
|
If missingFiles.Count > 0 Then
|
|
g.RunTask(Sub() MsgError("Source file not found!", $"{missingFiles.Join(BR)}{BR}could not be found and got skipped!"))
|
|
Exit Sub
|
|
End If
|
|
|
|
g.MainForm.OpenVideoSourceFiles(p.SourceFiles, True)
|
|
g.ProjectPath = p.TempDir + p.TargetFile.Base + ".srip"
|
|
p.BatchMode = False
|
|
g.MainForm.SaveProjectPath(g.ProjectPath)
|
|
Else
|
|
If Not File.Exists(p.SourceFile) Then
|
|
g.RunTask(Sub() MsgError("Source file not found!", $"'{p.SourceFile}'{BR}could not be found and got skipped!"))
|
|
Exit Sub
|
|
End If
|
|
End If
|
|
|
|
g.RaiseAppEvent(ApplicationEvent.BeforeProcessing)
|
|
|
|
Log.WriteConfiguration()
|
|
|
|
Log.WriteHeader($"{p.Script.Engine} Script")
|
|
Log.WriteLine(p.Script.GetFullScript)
|
|
|
|
Dim err = p.Script.GetError
|
|
|
|
If err <> "" Then
|
|
Throw New ErrorAbortException($"{p.Script.Engine} Script Error", err)
|
|
End If
|
|
|
|
Log.WriteHeader("Source Script Info")
|
|
Log.WriteLine(p.SourceScript.GetInfo().GetInfoText(-1))
|
|
Log.WriteHeader("Target Script Info")
|
|
Log.WriteLine(p.Script.GetInfo().GetInfoText(-1))
|
|
|
|
g.MainForm.Hide()
|
|
|
|
Dim actions As New List(Of Action)
|
|
|
|
If p.SkipAudioEncoding AndAlso File.Exists(p.Audio0.GetOutputFile) Then
|
|
p.Audio0.File = p.Audio0.GetOutputFile()
|
|
Else
|
|
actions.Add(Sub()
|
|
Audio.Process(p.Audio0)
|
|
p.Audio0.Encode()
|
|
End Sub)
|
|
End If
|
|
|
|
If p.SkipAudioEncoding AndAlso File.Exists(p.Audio1.GetOutputFile) Then
|
|
p.Audio1.File = p.Audio1.GetOutputFile()
|
|
Else
|
|
actions.Add(Sub()
|
|
Audio.Process(p.Audio1)
|
|
p.Audio1.Encode()
|
|
End Sub)
|
|
End If
|
|
|
|
For Each track In p.AudioTracks
|
|
Dim temp = track
|
|
|
|
If p.SkipAudioEncoding AndAlso File.Exists(track.GetOutputFile) Then
|
|
track.File = track.GetOutputFile()
|
|
Else
|
|
actions.Add(Sub()
|
|
Audio.Process(temp)
|
|
temp.Encode()
|
|
End Sub)
|
|
End If
|
|
Next
|
|
|
|
actions.Add(Sub() Subtitle.Cut(p.VideoEncoder.Muxer.Subtitles))
|
|
|
|
If CanEncodeVideo() Then
|
|
If p.VideoEncoder.CanChunkEncode Then
|
|
p.Script.Synchronize()
|
|
|
|
For Each i In p.VideoEncoder.GetChunkEncodeActions
|
|
actions.Add(i)
|
|
Next
|
|
Else
|
|
actions.Add(AddressOf p.VideoEncoder.Encode)
|
|
End If
|
|
End If
|
|
|
|
Try
|
|
Parallel.Invoke(New ParallelOptions With {.MaxDegreeOfParallelism = s.ParallelProcsNum}, actions.ToArray)
|
|
Catch ex As AggregateException
|
|
ExceptionDispatchInfo.Capture(ex.InnerExceptions(0)).Throw()
|
|
End Try
|
|
|
|
Log.Save()
|
|
|
|
g.RaiseAppEvent(ApplicationEvent.AfterVideoEncoded)
|
|
|
|
p.VideoEncoder.Muxer.Mux()
|
|
|
|
Dim cts = New CancellationTokenSource()
|
|
Dim closingTD As TaskDialog(Of DialogResult)
|
|
Dim mainFormClosingHandler As FormClosingEventHandler
|
|
|
|
If p.Thumbnailer Then
|
|
mainFormClosingHandler = Sub(sender As Object, e As FormClosingEventArgs)
|
|
If Not e.Cancel Then
|
|
Using closingTD
|
|
closingTD = New TaskDialog(Of DialogResult)(DialogResult.Yes)
|
|
closingTD.Icon = TaskIcon.Warning
|
|
closingTD.Title = "Close while Thumbnailer is running?"
|
|
closingTD.Content = "StaxRip shall be closed while Thumbnailer is still running."
|
|
closingTD.Content += $"{BR2}These are your options:"
|
|
closingTD.Content += $"{BR}(1) Leave this dialog open and StaxRip will continue the closing procedure as soon as Thumbnailer finishes."
|
|
closingTD.Content += $" If you have modified the project, you may be asked to save it first."
|
|
closingTD.Content += $"{BR}(2) YES - Forces Thumbnailer to stop immediately and StaxRip will continue closing."
|
|
closingTD.Content += $"{BR}(3) NO - Cancels the closing procedure and StaxRip keeps running."
|
|
closingTD.AddButton(DialogResult.Yes)
|
|
closingTD.AddButton(DialogResult.No)
|
|
|
|
If closingTD.Show() = DialogResult.No Then
|
|
e.Cancel = True
|
|
End If
|
|
End Using
|
|
End If
|
|
End Sub
|
|
|
|
Dim thumbnailerTask = Task.Run(
|
|
Async Function()
|
|
AddHandler g.MainForm.FormClosing, mainFormClosingHandler
|
|
|
|
Dim proceededSources = Await Thumbnailer.RunAsync(cts.Token, p, p.TargetFile)
|
|
|
|
If closingTD IsNot Nothing AndAlso Not closingTD.IsDisposingOrDisposed Then
|
|
closingTD.Close()
|
|
End If
|
|
|
|
RemoveHandler g.MainForm.FormClosing, mainFormClosingHandler
|
|
Return proceededSources
|
|
End Function,
|
|
cts.Token)
|
|
End If
|
|
|
|
g.RaiseAppEvent(ApplicationEvent.AfterJobMuxed)
|
|
|
|
Log.WriteHeader("Job Complete")
|
|
Log.WriteStats(startTime)
|
|
Log.Save()
|
|
|
|
g.ArchiveLogFile(Log.GetPath)
|
|
g.DeleteTempFiles()
|
|
g.RaiseAppEvent(ApplicationEvent.AfterJobProcessed)
|
|
JobManager.RemoveJob(jobPath)
|
|
|
|
If jobPath.StartsWith(Folder.Settings + "Batch Projects\") Then
|
|
File.Delete(jobPath)
|
|
End If
|
|
Catch ex As SkipException
|
|
Log.Save()
|
|
ProcController.Aborted = False
|
|
Catch ex As ErrorAbortException
|
|
Log.Save()
|
|
g.RaiseAppEvent(ApplicationEvent.AfterJobFailed)
|
|
g.ShowException(ex, Nothing, Nothing, 50)
|
|
g.ShellExecute(g.GetTextEditorPath(), """" + p.TempDir + p.TargetFile.Base + "_staxrip.log" + """")
|
|
ProcController.Aborted = False
|
|
End Try
|
|
End Sub
|
|
|
|
Function CanEncodeVideo() As Boolean
|
|
Return Not (p.SkipVideoEncoding AndAlso Not TypeOf p.VideoEncoder Is NullEncoder AndAlso
|
|
File.Exists(p.VideoEncoder.OutputPath))
|
|
End Function
|
|
|
|
Sub DeleteTempFiles()
|
|
If s.DeleteTempFilesMode <> DeleteMode.Disabled AndAlso p.TempDir.EndsWith("_temp\") Then
|
|
Try
|
|
Dim moreJobsToProcessInTempDir = JobManager.GetJobs.Where(Function(a) a.Active AndAlso a.Path.Contains(p.TempDir))
|
|
|
|
If moreJobsToProcessInTempDir.Count = 0 Then
|
|
If s.DeleteTempFilesMode = DeleteMode.RecycleBin Then
|
|
FolderHelp.Delete(p.TempDir, RecycleOption.SendToRecycleBin)
|
|
Else
|
|
FolderHelp.Delete(p.TempDir)
|
|
End If
|
|
End If
|
|
Catch
|
|
End Try
|
|
End If
|
|
End Sub
|
|
|
|
ReadOnly Property StartupTemplatePath() As String
|
|
Get
|
|
Dim ret = Folder.Template + s.StartupTemplate + ".srip"
|
|
|
|
If Not File.Exists(ret) Then
|
|
ret = Folder.Template + "Automatic Workflow.srip"
|
|
s.StartupTemplate = "Automatic Workflow"
|
|
End If
|
|
|
|
Return ret
|
|
End Get
|
|
End Property
|
|
|
|
ReadOnly Property SettingsFile() As String
|
|
Get
|
|
Return Folder.Settings + "Settings.dat"
|
|
End Get
|
|
End Property
|
|
|
|
Function BrowseFolder(defaultFolder As String) As String
|
|
Using dialog As New FolderBrowserDialog
|
|
dialog.SetSelectedPath(defaultFolder)
|
|
|
|
If dialog.ShowDialog = DialogResult.OK Then
|
|
Return dialog.SelectedPath
|
|
End If
|
|
End Using
|
|
End Function
|
|
|
|
Function VerifyRequirements() As Boolean
|
|
If s.VerifyToolStatus AndAlso Environment.Is64BitProcess Then
|
|
SyncLock Package.ConfLock
|
|
For Each pack In Package.Items.Values
|
|
If Not pack.VerifyOK Then
|
|
Return False
|
|
End If
|
|
Next
|
|
End SyncLock
|
|
|
|
If Not p.Script.IsFilterActive("Source") Then
|
|
MsgWarn("No active filter of category 'Source' found.")
|
|
Return False
|
|
End If
|
|
End If
|
|
|
|
Return True
|
|
End Function
|
|
|
|
Function VerifySource(files As IEnumerable(Of String)) As Boolean
|
|
For Each file In files
|
|
If Not file.IsProcessEncodingCompatible AndAlso p.Script.IsAviSynth Then
|
|
ShowAviSynthUnicodeError()
|
|
Return False
|
|
End If
|
|
Next
|
|
|
|
Return True
|
|
End Function
|
|
|
|
Sub ShowAviSynthUnicodeError()
|
|
MsgError($"Unicode filenames are not supported by AviSynth unless Windows 10 is used." + BR2 +
|
|
$"Rename the file or enable VapourSynth:{BR2}Filters > Filter Setup > VapourSynth")
|
|
End Sub
|
|
|
|
Sub PlayAudio(ap As AudioProfile)
|
|
If FileTypes.AudioRaw.Contains(ap.File.Ext) Then
|
|
g.ShellExecute(Package.mpvnet.Path, ap.File.Escape)
|
|
ElseIf ap.File = p.FirstOriginalSourceFile AndAlso ap.Streams.Count > 0 Then
|
|
g.ShellExecute(Package.mpvnet.Path, "--audio=" & (ap.Stream.Index + 1) & " " + p.FirstOriginalSourceFile.Escape)
|
|
ElseIf FileTypes.Audio.Contains(ap.File.Ext) Then
|
|
g.ShellExecute(Package.mpvnet.Path, "--audio-delay=" + (g.ExtractDelay(ap.File) / 1000).ToInvariantString.Shorten(9) + " --audio-files=" + ap.File.Escape + " " + p.FirstOriginalSourceFile.Escape)
|
|
Else
|
|
MsgError("Unable to play audio.")
|
|
End If
|
|
End Sub
|
|
|
|
Sub PlayScript(script As VideoScript)
|
|
PlayScriptWithMPV(script)
|
|
End Sub
|
|
|
|
Function GetAudioProfileForScriptPlayback() As AudioProfile
|
|
If File.Exists(p.Audio0.File) AndAlso FileTypes.Audio.Contains(p.Audio0.File.Ext) Then
|
|
Return p.Audio0
|
|
ElseIf File.Exists(p.Audio1.File) AndAlso FileTypes.Audio.Contains(p.Audio1.File.Ext) Then
|
|
Return p.Audio1
|
|
End If
|
|
End Function
|
|
|
|
Sub PlayScriptWithMPC(script As VideoScript, Optional cliArgs As String = Nothing)
|
|
If script Is Nothing Then
|
|
Exit Sub
|
|
End If
|
|
|
|
script.Synchronize()
|
|
Dim playerPath = Package.MPC.Path
|
|
|
|
If Not playerPath.FileExists AndAlso Package.MPC.VerifyOK(True) Then
|
|
playerPath = Package.MPC.Path
|
|
End If
|
|
|
|
If Not playerPath.FileExists Then
|
|
Exit Sub
|
|
End If
|
|
|
|
Dim args As String
|
|
|
|
If cliArgs <> "" Then
|
|
args += " " + cliArgs
|
|
End If
|
|
|
|
Dim ap = GetAudioProfileForScriptPlayback()
|
|
|
|
If Not ap Is Nothing AndAlso FileTypes.Audio.Contains(ap.File.Ext) AndAlso
|
|
p.Ranges.Count = 0 Then
|
|
|
|
args += " /dub " + ap.File.Escape
|
|
End If
|
|
|
|
args += " " + script.Path.Escape
|
|
g.ShellExecute(playerPath, args.Trim)
|
|
End Sub
|
|
|
|
Sub PlayScriptWithMPV(script As VideoScript, Optional cliArgs As String = Nothing)
|
|
If script Is Nothing Then
|
|
Exit Sub
|
|
End If
|
|
|
|
script.Synchronize()
|
|
Dim args As String
|
|
|
|
If script.Engine = ScriptEngine.VapourSynth Then
|
|
args += " --demuxer-lavf-format=vapoursynth"
|
|
End If
|
|
|
|
If cliArgs <> "" Then
|
|
args += " " + cliArgs
|
|
End If
|
|
|
|
If Calc.IsARSignalingRequired Then
|
|
args += " --video-aspect=" + Calc.GetTargetDAR.ToInvariantString.Shorten(8)
|
|
End If
|
|
|
|
Dim ap = GetAudioProfileForScriptPlayback()
|
|
|
|
If Not ap Is Nothing AndAlso FileTypes.Audio.Contains(ap.File.Ext) AndAlso p.Ranges.Count = 0 Then
|
|
args += " --audio-files=" + ap.File.Escape
|
|
End If
|
|
|
|
args += " " + script.Path.Escape
|
|
g.Execute(Package.mpvnet.Path, args.Trim)
|
|
End Sub
|
|
|
|
Function ExtractDelay(value As String) As Integer
|
|
Dim match = Regex.Match(value, " (-?\d+) ?ms")
|
|
|
|
If match.Success Then
|
|
Return CInt(match.Groups(1).Value)
|
|
End If
|
|
End Function
|
|
|
|
Function GetSourceBase() As String
|
|
If p.TempDir.EndsWithEx("_temp\") Then
|
|
Return "temp"
|
|
Else
|
|
Return p.SourceFile.Base
|
|
End If
|
|
End Function
|
|
|
|
Sub ShowCode(title As String, content As String, Optional find As String = Nothing, Optional wordwrap As Boolean = False)
|
|
Dim form As New CodeForm(content, find, wordwrap)
|
|
form.Text = title
|
|
form.Show()
|
|
End Sub
|
|
|
|
Sub ShowHelp(title As String, content As String)
|
|
If title <> "" Then
|
|
title = title.TrimEnd("."c, ":"c)
|
|
End If
|
|
|
|
MsgInfo(title, content)
|
|
End Sub
|
|
|
|
Sub PopulateProfileMenu(
|
|
ic As ToolStripItemCollection,
|
|
profiles As IList,
|
|
dialogAction As Action,
|
|
loadAction As Action(Of Profile))
|
|
|
|
For Each iProfile As Profile In profiles
|
|
Dim a = iProfile.Name.SplitNoEmpty("|")
|
|
Dim l = ic
|
|
|
|
For i = 0 To a.Length - 1
|
|
Dim found = False
|
|
|
|
For Each iItem As ToolStripItem In l
|
|
If i < a.Length - 1 Then
|
|
If iItem.Text = a(i) + " " Then
|
|
found = True
|
|
l = DirectCast(iItem, ToolStripMenuItem).DropDownItems
|
|
End If
|
|
End If
|
|
Next
|
|
|
|
If Not found Then
|
|
If i = a.Length - 1 Then
|
|
Dim item As New MenuItemEx(a(i) + " ", Sub() loadAction(iProfile))
|
|
l.Add(item)
|
|
l = item.DropDownItems
|
|
Else
|
|
Dim item As New MenuItemEx(a(i) + " ")
|
|
l.Add(item)
|
|
l = item.DropDownItems
|
|
End If
|
|
End If
|
|
Next
|
|
Next
|
|
|
|
If Not dialogAction Is Nothing Then
|
|
ic.Add(New ToolStripSeparator)
|
|
ic.Add(New MenuItemEx("Edit Profiles...", dialogAction, "Opens the profiles editor"))
|
|
End If
|
|
End Sub
|
|
|
|
Function GetAutoSize(percentage As Integer) As Integer
|
|
Dim ret As Integer
|
|
Dim size = p.TargetSize
|
|
Dim bitrate = p.VideoBitrate
|
|
|
|
For i = 1 To 100000
|
|
p.TargetSize = i
|
|
p.VideoBitrate = CInt(Calc.GetVideoBitrate)
|
|
|
|
If CInt(Calc.GetPercent) >= percentage Then
|
|
ret = i
|
|
Exit For
|
|
End If
|
|
Next
|
|
|
|
p.TargetSize = size
|
|
p.VideoBitrate = bitrate
|
|
|
|
If ret = 0 Then ret = size
|
|
|
|
Return ret
|
|
End Function
|
|
|
|
Function GetPreviewPosMS() As Integer
|
|
Return CInt((s.LastPosition / p.Script.GetCachedFrameRate) * 1000)
|
|
End Function
|
|
|
|
Function GetTextEditorPath() As String
|
|
Dim ret = GetAppPathForExtension("txt")
|
|
|
|
If ret <> "" Then
|
|
Return ret
|
|
End If
|
|
|
|
Return "notepad.exe"
|
|
End Function
|
|
|
|
Function GetAppPathForExtension(ParamArray extensions As String()) As String
|
|
For Each extension In extensions
|
|
If Not extension.StartsWith(".") Then
|
|
extension = "." + extension
|
|
End If
|
|
|
|
Dim c = 0UI
|
|
|
|
If Native.AssocQueryString(&H40, 2, extension, Nothing, Nothing, c) = 1 Then
|
|
If c > 0 Then
|
|
Dim sb As New StringBuilder(CInt(c))
|
|
|
|
If 0 = Native.AssocQueryString(&H40, 2, extension, Nothing, sb, c) Then
|
|
Dim ret = sb.ToString
|
|
|
|
If File.Exists(ret) Then
|
|
Return ret
|
|
End If
|
|
End If
|
|
End If
|
|
End If
|
|
Next
|
|
End Function
|
|
|
|
Sub SaveSettings()
|
|
Try
|
|
Using mutex As New Mutex(False, "staxrip settings file")
|
|
mutex.WaitOne()
|
|
SafeSerialization.Serialize(s, g.SettingsFile)
|
|
mutex.ReleaseMutex()
|
|
End Using
|
|
|
|
Dim backupPath = Folder.Settings + "Backup\"
|
|
|
|
If Not Directory.Exists(backupPath) Then
|
|
Directory.CreateDirectory(backupPath)
|
|
End If
|
|
|
|
FileHelp.Copy(g.SettingsFile, backupPath + "Settings(v" + Application.ProductVersion + ").dat")
|
|
Catch ex As Exception
|
|
g.ShowException(ex)
|
|
End Try
|
|
End Sub
|
|
|
|
Sub LoadVideoEncoder(profile As Profile)
|
|
Dim currentMuxer = p.VideoEncoder.Muxer
|
|
p.VideoEncoder = DirectCast(ObjectHelp.GetCopy(profile), VideoEncoder)
|
|
|
|
If currentMuxer.IsSupported(p.VideoEncoder.OutputExt) Then
|
|
p.VideoEncoder.Muxer = currentMuxer
|
|
Else
|
|
p.VideoEncoder.Muxer.Init()
|
|
End If
|
|
|
|
MainForm.tbTargetFile.Text = p.TargetFile.ChangeExt(p.VideoEncoder.Muxer.OutputExt)
|
|
p.VideoEncoder.OnStateChange()
|
|
p.VideoEncoder.SetMetaData(p.LastOriginalSourceFile)
|
|
MainForm.RecalcBitrate()
|
|
MainForm.Assistant()
|
|
End Sub
|
|
|
|
Sub LoadAudioProfile0(profile As Profile)
|
|
Dim file = p.Audio0.File
|
|
Dim delay = p.Audio0.Delay
|
|
Dim language = p.Audio0.Language
|
|
Dim stream = p.Audio0.Stream
|
|
Dim streams = p.Audio0.Streams
|
|
p.Audio0 = DirectCast(ObjectHelp.GetCopy(profile), AudioProfile)
|
|
p.Audio0.File = file
|
|
p.Audio0.Language = language
|
|
p.Audio0.Stream = stream
|
|
p.Audio0.Streams = streams
|
|
p.Audio0.Delay = delay
|
|
g.MainForm.llAudioProfile0.Text = g.ConvertPath(p.Audio0.Name)
|
|
g.MainForm.UpdateSizeOrBitrate()
|
|
g.MainForm.Assistant()
|
|
End Sub
|
|
|
|
Sub LoadAudioProfile1(profile As Profile)
|
|
Dim file = p.Audio1.File
|
|
Dim delay = p.Audio1.Delay
|
|
Dim language = p.Audio1.Language
|
|
Dim stream = p.Audio1.Stream
|
|
Dim streams = p.Audio1.Streams
|
|
p.Audio1 = DirectCast(ObjectHelp.GetCopy(profile), AudioProfile)
|
|
p.Audio1.File = file
|
|
p.Audio1.Language = language
|
|
p.Audio1.Stream = stream
|
|
p.Audio1.Streams = streams
|
|
p.Audio1.Delay = delay
|
|
g.MainForm.llAudioProfile1.Text = g.ConvertPath(p.Audio1.Name)
|
|
g.MainForm.UpdateSizeOrBitrate()
|
|
g.MainForm.Assistant()
|
|
End Sub
|
|
|
|
Sub RaiseAppEvent(ae As ApplicationEvent)
|
|
Select Case ae
|
|
Case ApplicationEvent.AfterJobFailed
|
|
RaiseEvent AfterJobFailed()
|
|
Case ApplicationEvent.AfterJobMuxed
|
|
RaiseEvent AfterJobMuxed()
|
|
Case ApplicationEvent.AfterJobProcessed
|
|
RaiseEvent AfterJobProcessed()
|
|
Case ApplicationEvent.AfterJobsProcessed
|
|
RaiseEvent AfterJobsProcessed()
|
|
Case ApplicationEvent.AfterProjectLoaded
|
|
RaiseEvent AfterProjectLoaded()
|
|
Case ApplicationEvent.AfterProjectOrSourceLoaded
|
|
RaiseEvent AfterProjectOrSourceLoaded()
|
|
Case ApplicationEvent.AfterSourceLoaded
|
|
RaiseEvent AfterSourceLoaded()
|
|
Case ApplicationEvent.AfterVideoEncoded
|
|
RaiseEvent AfterVideoEncoded()
|
|
Case ApplicationEvent.ApplicationExit
|
|
RaiseEvent ApplicationExit()
|
|
Case ApplicationEvent.BeforeJobProcessed
|
|
RaiseEvent BeforeJobProcessed()
|
|
Case ApplicationEvent.BeforeProcessing
|
|
RaiseEvent BeforeProcessing()
|
|
End Select
|
|
|
|
Dim scriptPath = Folder.Scripts + ae.ToString + ".ps1"
|
|
|
|
If File.Exists(scriptPath) Then
|
|
g.DefaultCommands.ExecutePowerShellFile(scriptPath)
|
|
End If
|
|
|
|
For Each ec In s.EventCommands
|
|
If ec.Enabled AndAlso ec.Event = ae Then
|
|
Dim matches = 0
|
|
|
|
For Each criteria In ec.CriteriaList
|
|
criteria.PropertyString = Macro.Expand(criteria.Macro)
|
|
|
|
If criteria.Eval Then
|
|
matches += 1
|
|
End If
|
|
Next
|
|
|
|
If (ec.CriteriaList.Count = 0 OrElse (ec.OrOnly AndAlso matches > 0) OrElse
|
|
(Not ec.OrOnly AndAlso matches = ec.CriteriaList.Count)) AndAlso
|
|
Not ec.CommandParameters Is Nothing Then
|
|
|
|
Dim command = g.MainForm.CustomMainMenu.CommandManager.GetCommand(ec.CommandParameters.MethodName)
|
|
|
|
If s.LogEventCommand AndAlso p.SourceFile <> "" Then
|
|
Log.WriteHeader("Event Command: " + ec.Name)
|
|
Log.WriteLine("Event: " + DispNameAttribute.GetValueForEnum(ec.Event))
|
|
Log.WriteLine("Command: " + command.MethodInfo.Name)
|
|
Log.WriteLine(command.GetParameterHelp(ec.CommandParameters.Parameters))
|
|
End If
|
|
|
|
g.MainForm.CustomMainMenu.CommandManager.Process(ec.CommandParameters)
|
|
End If
|
|
End If
|
|
Next
|
|
End Sub
|
|
|
|
Sub SetTempDir()
|
|
If p.SourceFile <> "" Then
|
|
If p.NoTempDir Then
|
|
p.TempDir = p.SourceFile.Dir
|
|
Else
|
|
p.TempDir = Macro.Expand(p.TempDir)
|
|
|
|
If p.TempDir = "" Then
|
|
If p.SourceFile.Dir.EndsWith("_temp\") Then
|
|
p.TempDir = p.SourceFile.Dir
|
|
Else
|
|
p.TempDir = p.SourceFile.Dir + p.SourceFile.Base + "_temp\"
|
|
End If
|
|
End If
|
|
|
|
p.TempDir = p.TempDir.FixDir
|
|
|
|
If Not Directory.Exists(p.TempDir) Then
|
|
Try
|
|
Directory.CreateDirectory(p.TempDir)
|
|
Catch
|
|
Try
|
|
p.TempDir = p.SourceFile.DirAndBase + "_temp\"
|
|
|
|
If Not Directory.Exists(p.TempDir) Then
|
|
Directory.CreateDirectory(p.TempDir)
|
|
End If
|
|
Catch
|
|
MsgWarn("Failed to create a temp directory. By default it's created " +
|
|
"in the directory of the source file so it's not possible " +
|
|
"to open files directly from an optical drive unless a temp directory " +
|
|
"is defined in the options. Usually discs are copied to the hard drive " +
|
|
"first using an application like MakeMKV, DVDFab or AnyDVD.")
|
|
Throw New AbortException
|
|
End Try
|
|
End Try
|
|
End If
|
|
End If
|
|
End If
|
|
End Sub
|
|
|
|
Sub ShowCommandLinePreview(title As String, value As String)
|
|
If s.CommandLinePreviewViaCodeForm Then
|
|
ShowCodePreview(value, Nothing, True)
|
|
Else
|
|
Environment.SetEnvironmentVariable("CommandLineToShow", BR + value + BR)
|
|
|
|
If s.PreferWindowsTerminal AndAlso g.IsWindowsTerminalAvailable Then
|
|
g.Execute("wt.exe", "powershell.exe -NoLogo -NoExit -NoProfile -Command $env:CommandLineToShow")
|
|
Else
|
|
g.Execute("powershell.exe", "-NoLogo -NoExit -NoProfile -Command $env:CommandLineToShow")
|
|
End If
|
|
End If
|
|
End Sub
|
|
|
|
Sub ShowCodePreview(code As String, Optional find As String = Nothing, Optional wordwrap As Boolean = False)
|
|
ShowCode("Code Preview", code, find, wordwrap)
|
|
End Sub
|
|
|
|
Sub ffmsindex(
|
|
sourcePath As String,
|
|
cachePath As String,
|
|
Optional indexAudio As Boolean = False,
|
|
Optional proj As Project = Nothing)
|
|
|
|
If File.Exists(sourcePath) AndAlso Not File.Exists(cachePath) AndAlso
|
|
Not FileTypes.VideoText.Contains(sourcePath.Ext) Then
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "Indexing using ffmsindex"
|
|
proc.SkipString = "Indexing, please wait..."
|
|
proc.Project = proj
|
|
proc.Priority = ProcessPriorityClass.Normal
|
|
proc.File = Package.ffms2.Directory + "ffmsindex.exe"
|
|
proc.Arguments = If(indexAudio, "-t -1 ", "") + sourcePath.LongPathPrefix.Escape +
|
|
" " + cachePath.LongPathPrefix.Escape
|
|
proc.Start()
|
|
End Using
|
|
End If
|
|
End Sub
|
|
|
|
Function IsValidSource(Optional warn As Boolean = True) As Boolean
|
|
If p.SourceScript.GetFrameCount = 0 Then
|
|
If warn Then
|
|
MsgWarn("Failed to load source.")
|
|
End If
|
|
|
|
Return False
|
|
End If
|
|
|
|
If p.SourceScript.GetError <> "" Then
|
|
MsgError("Script Error", p.SourceScript.GetError)
|
|
Return False
|
|
End If
|
|
|
|
Return True
|
|
End Function
|
|
|
|
Function IsSourceSameOrSimilar(path As String) As Boolean
|
|
Return IsSourceSame(path) OrElse IsSourceSimilar(path)
|
|
End Function
|
|
|
|
Function IsCOMObjectRegistered(guid As String) As Boolean
|
|
Return File.Exists(Registry.ClassesRoot.GetString("CLSID\" + guid + "\InprocServer32", Nothing))
|
|
End Function
|
|
|
|
Function IsSourceSame(path As String) As Boolean
|
|
Return path.Base.StartsWith(p.SourceFile.Base) OrElse path.StartsWithEx(p.TempDir)
|
|
End Function
|
|
|
|
Function GetFilesInTempDirAndParent() As List(Of String)
|
|
Dim ret As New List(Of String)
|
|
Dim dirs As New HashSet(Of String)
|
|
|
|
If p.TempDir <> "" Then
|
|
dirs.Add(p.TempDir)
|
|
End If
|
|
|
|
If p.TempDir?.EndsWith("_temp\") Then
|
|
dirs.Add(p.TempDir.Parent)
|
|
End If
|
|
|
|
dirs.Add(p.FirstOriginalSourceFile.Dir)
|
|
|
|
For Each i In dirs
|
|
ret.AddRange(Directory.GetFiles(i))
|
|
Next
|
|
|
|
Return ret
|
|
End Function
|
|
|
|
Function IsSourceSimilar(path As String) As Boolean
|
|
If p.SourceFile.Contains("_") Then
|
|
Dim sourceBase = p.SourceFile.Base
|
|
|
|
While sourceBase.Length > 2 AndAlso sourceBase.ToCharArray.Last.IsDigit
|
|
sourceBase = sourceBase.DeleteRight(1)
|
|
End While
|
|
|
|
If sourceBase.EndsWith("_") AndAlso path.Base.StartsWith(sourceBase.TrimEnd("_"c)) Then
|
|
Return True
|
|
End If
|
|
End If
|
|
End Function
|
|
|
|
Function IsCulture(twoLetterCode As String) As Boolean
|
|
Return CultureInfo.CurrentCulture.TwoLetterISOLanguageName = twoLetterCode
|
|
End Function
|
|
|
|
Private ExceptionHandled As Boolean
|
|
|
|
Sub OnException(ex As Exception)
|
|
If ExceptionHandled Then
|
|
Exit Sub
|
|
Else
|
|
ExceptionHandled = True
|
|
End If
|
|
|
|
If Not TypeOf ex Is AbortException Then
|
|
Try
|
|
If File.Exists(p.SourceFile) Then
|
|
Dim name = p.TargetFile.Base
|
|
|
|
If name = "" Then
|
|
name = p.SourceFile.Base
|
|
End If
|
|
|
|
g.MainForm.SaveProjectPath(p.SourceFile.Dir + "recovery.srip")
|
|
End If
|
|
|
|
g.SaveSettings()
|
|
Catch
|
|
End Try
|
|
|
|
Try
|
|
ShowException(ex)
|
|
MakeBugReport(ex)
|
|
Catch
|
|
End Try
|
|
|
|
Process.GetCurrentProcess.Kill()
|
|
End If
|
|
End Sub
|
|
|
|
Sub ShowException(
|
|
ex As Exception,
|
|
Optional title As String = Nothing,
|
|
Optional content As String = Nothing,
|
|
Optional timeout As Integer = 0)
|
|
|
|
Try
|
|
Using td As New TaskDialog(Of String)
|
|
If title = "" Then
|
|
If TypeOf ex Is ErrorAbortException Then
|
|
td.Title = DirectCast(ex, ErrorAbortException).Title + $" (v{Application.ProductVersion})"
|
|
Else
|
|
td.Title = ex.GetType.Name + $" (v{Application.ProductVersion})"
|
|
End If
|
|
Else
|
|
td.Title = title
|
|
End If
|
|
|
|
td.Timeout = timeout
|
|
td.Content = (ex.Message + BR2 + content).Trim
|
|
td.Icon = TaskIcon.Error
|
|
td.ExpandedContent = ex.ToString
|
|
td.ShowCopyButton = True
|
|
td.AddButton("OK")
|
|
td.Show()
|
|
End Using
|
|
Catch
|
|
Dim msg As String
|
|
|
|
If TypeOf ex Is ErrorAbortException Then
|
|
msg = DirectCast(ex, ErrorAbortException).Title
|
|
Else
|
|
msg = ex.GetType.Name
|
|
End If
|
|
|
|
VB6.MsgBox(msg + BR2 + ex.Message + BR2 + ex.ToString, VB6.MsgBoxStyle.Critical)
|
|
End Try
|
|
End Sub
|
|
|
|
Sub ArchiveLogFile(path As String)
|
|
Try
|
|
Dim logFolder = Folder.Settings + "Log Files\"
|
|
|
|
If Not Directory.Exists(logFolder) Then
|
|
Directory.CreateDirectory(logFolder)
|
|
End If
|
|
|
|
FileHelp.Copy(path, logFolder + Date.Now.ToString("yyyy-MM-dd - HH.mm.ss") + " - " + path.FileName)
|
|
Dim di As New DirectoryInfo(logFolder)
|
|
|
|
While di.GetFiles("*.log").Length > s.LogFileNum
|
|
FileHelp.Delete(di.GetFiles("*.log").OrderBy(Function(val) val.LastWriteTime).First.FullName)
|
|
End While
|
|
Catch ex As Exception
|
|
ShowException(ex, "Failed to archive log file")
|
|
End Try
|
|
End Sub
|
|
|
|
Sub SetRenderer(ms As ToolStrip)
|
|
If VisualStyleInformation.IsEnabledByUser Then
|
|
ms.Renderer = New ToolStripRendererEx()
|
|
Else
|
|
ms.Renderer = New ToolStripSystemRenderer()
|
|
End If
|
|
End Sub
|
|
|
|
Sub Play(file As String)
|
|
g.ShellExecute(Package.mpvnet.Path.Escape, file.Escape)
|
|
End Sub
|
|
|
|
Sub ShowWikiPage(title As String)
|
|
ShellExecute($"https://github.com/staxrip/staxrip/wiki/{title}")
|
|
End Sub
|
|
|
|
Sub OnUnhandledException(sender As Object, e As ThreadExceptionEventArgs)
|
|
OnException(e.Exception)
|
|
End Sub
|
|
|
|
Sub OnUnhandledException(sender As Object, e As UnhandledExceptionEventArgs)
|
|
OnException(DirectCast(e.ExceptionObject, Exception))
|
|
End Sub
|
|
|
|
Private IconValue As Icon
|
|
Private LastIconFile As String
|
|
|
|
Public Property Icon As Icon
|
|
Get
|
|
If IconValue Is Nothing OrElse s.IconFile <> LastIconFile Then
|
|
If File.Exists(s.IconFile) Then
|
|
IconValue = New Icon(s.IconFile)
|
|
LastIconFile = s.IconFile
|
|
Else
|
|
IconValue = My.Resources.Black
|
|
End If
|
|
End If
|
|
|
|
Return IconValue
|
|
End Get
|
|
Set(value As Icon)
|
|
IconValue = value
|
|
End Set
|
|
End Property
|
|
|
|
Sub MakeBugReport(e As Exception)
|
|
If e Is Nothing AndAlso Not g.IsValidSource(False) Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not e Is Nothing Then
|
|
If Log.IsEmpty Then Log.WriteEnvironment()
|
|
Log.WriteHeader("Exception")
|
|
Log.WriteLine(e.ToString)
|
|
End If
|
|
|
|
Log.Save(p)
|
|
|
|
Dim logfileOpened = False
|
|
Dim fp = Log.GetPath
|
|
|
|
If MsgQuestion("An error occured", "Do you want to open the log file?",
|
|
TaskButton.YesNo) = DialogResult.Yes Then
|
|
|
|
g.ShellExecute(g.GetTextEditorPath(), fp.Escape)
|
|
logfileOpened = True
|
|
End If
|
|
|
|
If MsgQuestion("Bug Report", "Do you want to report an issue or bug?",
|
|
TaskButton.YesNo) = DialogResult.Yes Then
|
|
|
|
If Not logfileOpened Then
|
|
g.ShellExecute(g.GetTextEditorPath(), fp.Escape)
|
|
End If
|
|
|
|
g.SelectFileWithExplorer(fp)
|
|
g.ShellExecute("https://github.com/staxrip/staxrip/issues")
|
|
End If
|
|
End Sub
|
|
|
|
Function FileExists(path As String) As Boolean
|
|
For x = 1 To 6
|
|
If File.Exists(path) Then
|
|
Return True
|
|
End If
|
|
|
|
Thread.Sleep(500)
|
|
Next
|
|
End Function
|
|
|
|
Sub ShutdownPC()
|
|
ShutdownPC(CType(Registry.CurrentUser.GetInt("Software\" + Application.ProductName, "ShutdownMode"), ShutdownMode))
|
|
End Sub
|
|
|
|
Sub ShutdownPC(mode As ShutdownMode)
|
|
If mode <> ShutdownMode.Nothing Then
|
|
g.SavedProject = p
|
|
g.MainForm.Close()
|
|
Registry.CurrentUser.Write("Software\" + Application.ProductName, "ShutdownMode", 0)
|
|
Shutdown.Commit(mode)
|
|
End If
|
|
End Sub
|
|
|
|
Function EnableFilter(search As String) As Boolean
|
|
For Each filter In p.Script.Filters
|
|
If filter.Category = search Then
|
|
If Not filter.Active Then
|
|
filter.Active = True
|
|
g.MainForm.FiltersListView.Load()
|
|
End If
|
|
|
|
Return True
|
|
End If
|
|
Next
|
|
End Function
|
|
|
|
Function BrowseFile(filter As String) As String
|
|
Using dialog As New OpenFileDialog
|
|
dialog.Filter = filter
|
|
dialog.SetInitDir(p.TempDir)
|
|
|
|
If dialog.ShowDialog = DialogResult.OK Then
|
|
Return dialog.FileName
|
|
End If
|
|
End Using
|
|
End Function
|
|
|
|
Sub ShowScriptInfo(script As VideoScript)
|
|
script.Synchronize()
|
|
|
|
If Not script.Path.FileExists Then
|
|
Exit Sub
|
|
End If
|
|
|
|
Dim text = If(script.Error = "", script.Info.GetInfoText(-1), script.Error)
|
|
text = BR + " " + text.FixBreak.Replace(BR, BR + " ") + BR
|
|
|
|
Using form As New StringEditorForm
|
|
form.ScaleClientSize(25, 15)
|
|
form.MaximizeBox = False
|
|
form.rtb.ReadOnly = True
|
|
form.cbWrap.Checked = False
|
|
form.cbWrap.Visible = False
|
|
form.rtb.Text = text
|
|
form.Text = script.Path
|
|
form.bnOK.Visible = False
|
|
form.bnCancel.Text = "Close"
|
|
form.ShowDialog()
|
|
End Using
|
|
End Sub
|
|
|
|
Sub AddHardcodedSubtitle()
|
|
Dim validSubtitles = p.VideoEncoder?.Muxer?.Subtitles?.Where(Function(x) x.Path.Ext().ToLowerEx().EqualsAny("srt", "ass", "idx"))
|
|
Dim enabledValidSubtitles = validSubtitles?.Where(Function(x) x.Enabled)
|
|
Dim selectedSubtitle As Subtitle = If(enabledValidSubtitles?.Any(), enabledValidSubtitles?.FirstOrDefault(), validSubtitles?.FirstOrDefault())
|
|
|
|
If selectedSubtitle IsNot Nothing Then
|
|
selectedSubtitle.Enabled = False
|
|
p.AddHardcodedSubtitleFilter(selectedSubtitle.Path, False)
|
|
End If
|
|
End Sub
|
|
|
|
Sub RunAutoCrop(progressAction As Action(Of Double))
|
|
p.SourceScript.Synchronize(True, True, True)
|
|
|
|
Using server = FrameServerFactory.Create(p.SourceScript.Path)
|
|
Dim len = server.Info.FrameCount \ (s.CropFrameCount + 1)
|
|
Dim crops(s.CropFrameCount - 1) As AutoCrop
|
|
Dim pos As Integer
|
|
|
|
For x = 1 To s.CropFrameCount
|
|
progressAction?.Invoke((x - 1) / s.CropFrameCount * 100)
|
|
pos = len * x
|
|
|
|
Using bmp = BitmapUtil.CreateBitmap(server, pos)
|
|
crops(x - 1) = AutoCrop.Start(bmp.Clone(New Rectangle(0, 0, bmp.Width, bmp.Height), PixelFormat.Format32bppRgb), pos)
|
|
End Using
|
|
Next
|
|
|
|
Dim leftCrops = crops.SelectMany(Function(arg) arg.Left).OrderBy(Function(arg) arg)
|
|
p.CropLeft = leftCrops(leftCrops.Count \ 10)
|
|
|
|
Dim topCrops = crops.SelectMany(Function(arg) arg.Top).OrderBy(Function(arg) arg)
|
|
p.CropTop = topCrops(topCrops.Count \ 10)
|
|
|
|
Dim rightCrops = crops.SelectMany(Function(arg) arg.Right).OrderBy(Function(arg) arg)
|
|
p.CropRight = rightCrops(rightCrops.Count \ 10)
|
|
|
|
Dim bottomCrops = crops.SelectMany(Function(arg) arg.Bottom).OrderBy(Function(arg) arg)
|
|
p.CropBottom = bottomCrops(bottomCrops.Count \ 10)
|
|
|
|
CorrectCropMod()
|
|
End Using
|
|
End Sub
|
|
|
|
Sub SmartCrop()
|
|
If Not p.Script.IsFilterActive("Resize") Then
|
|
Exit Sub
|
|
End If
|
|
|
|
Dim tempLeft = p.CropLeft
|
|
Dim tempRight = p.CropRight
|
|
|
|
Dim ae = Math.Abs(Calc.GetAspectRatioError)
|
|
|
|
While (p.SourceWidth - p.CropLeft - p.CropRight) > 64
|
|
p.CropLeft += 2
|
|
p.CropRight += 2
|
|
|
|
If Math.Abs(Calc.GetAspectRatioError()) < ae Then
|
|
tempLeft = p.CropLeft
|
|
tempRight = p.CropRight
|
|
ae = Math.Abs(Calc.GetAspectRatioError())
|
|
End If
|
|
End While
|
|
|
|
p.CropLeft = tempLeft
|
|
p.CropRight = tempRight
|
|
|
|
Dim tempTop = p.CropTop
|
|
Dim tempBottom = p.CropBottom
|
|
|
|
While (p.SourceHeight - p.CropTop - p.CropBottom) > 64
|
|
p.CropTop += 2
|
|
p.CropBottom += 2
|
|
|
|
If Math.Abs(Calc.GetAspectRatioError()) < ae Then
|
|
tempTop = p.CropTop
|
|
tempBottom = p.CropBottom
|
|
ae = Math.Abs(Calc.GetAspectRatioError())
|
|
End If
|
|
End While
|
|
|
|
p.CropTop = tempTop
|
|
p.CropBottom = tempBottom
|
|
End Sub
|
|
|
|
Sub OvercropWidth()
|
|
If p.AutoSmartOvercrop > 0 AndAlso
|
|
p.AutoSmartOvercrop < Calc.GetTargetDAR Then
|
|
|
|
Dim newar = p.AutoSmartOvercrop
|
|
Dim croph = p.SourceHeight - p.CropTop - p.CropBottom
|
|
g.MainForm.tbTargetHeight.Text = Calc.FixMod16(CInt(p.TargetWidth / newar)).ToString
|
|
newar = CSng(p.TargetWidth / p.TargetHeight)
|
|
Dim cropw = (newar / Calc.GetSourceDAR * (croph / p.SourceHeight)) * p.SourceWidth
|
|
p.CropLeft = CInt((p.SourceWidth - cropw) / 2)
|
|
p.CropRight = p.CropLeft
|
|
|
|
If p.CropLeft < 0 Then
|
|
p.CropLeft = 0
|
|
p.CropRight = 0
|
|
End If
|
|
|
|
CorrectCropMod()
|
|
End If
|
|
End Sub
|
|
|
|
Function ConvertPath(value As String) As String
|
|
If value = "" Then
|
|
Return ""
|
|
End If
|
|
|
|
If value.Length > 30 AndAlso value.Contains("|") Then
|
|
value = value.RightLast("|")
|
|
End If
|
|
|
|
If value.Contains("Constant Quality") Then
|
|
value = value.Replace("Constant Quality", "CQ")
|
|
End If
|
|
|
|
If value.Contains(" | ") Then
|
|
value = value.Replace(" | ", " - ")
|
|
End If
|
|
|
|
If value.Contains(" ") Then
|
|
value = value.Replace(" ", " ")
|
|
End If
|
|
|
|
Return value.Trim
|
|
End Function
|
|
|
|
Sub CorrectCropMod()
|
|
CorrectCropMod(False)
|
|
End Sub
|
|
|
|
Sub ForceCropMod()
|
|
If Not g.EnableFilter("Crop") Then
|
|
p.Script.InsertAfter("Source", New VideoFilter("Crop", "Crop", "Crop(%crop_left%, %crop_top%, -%crop_right%, -%crop_bottom%)"))
|
|
End If
|
|
|
|
CorrectCropMod(True)
|
|
End Sub
|
|
|
|
Sub CorrectCropMod(force As Boolean)
|
|
If p.AutoCorrectCropValues OrElse force Then
|
|
p.CropLeft += p.CropLeft Mod 2
|
|
p.CropRight += p.CropRight Mod 2
|
|
p.CropTop += p.CropTop Mod 2
|
|
p.CropBottom += p.CropBottom Mod 2
|
|
|
|
Dim modValue = 4
|
|
|
|
If Not p.Script.IsFilterActive("Resize") Then
|
|
modValue = p.ForcedOutputMod
|
|
End If
|
|
|
|
Dim whalf = ((p.SourceWidth - p.CropLeft - p.CropRight) Mod modValue) \ 2
|
|
|
|
If p.CropLeft > p.CropRight Then
|
|
p.CropLeft += whalf - whalf Mod 2
|
|
p.CropRight += whalf + whalf Mod 2
|
|
Else
|
|
p.CropRight += whalf - whalf Mod 2
|
|
p.CropLeft += whalf + whalf Mod 2
|
|
End If
|
|
|
|
Dim hhalf = ((p.SourceHeight - p.CropTop - p.CropBottom) Mod modValue) \ 2
|
|
|
|
If p.CropTop > p.CropBottom Then
|
|
p.CropTop += hhalf - hhalf Mod 2
|
|
p.CropBottom += hhalf + hhalf Mod 2
|
|
Else
|
|
p.CropBottom += hhalf - hhalf Mod 2
|
|
p.CropTop += hhalf + hhalf Mod 2
|
|
End If
|
|
|
|
g.MainForm.FiltersListView.Load()
|
|
End If
|
|
End Sub
|
|
|
|
Function GetTimeString(sec As Double) As String
|
|
Dim ts = TimeSpan.FromSeconds(sec)
|
|
|
|
Return CInt(Math.Floor(ts.TotalMinutes)).ToString("00") + ":" +
|
|
CInt(Math.Floor(ts.Seconds)).ToString("00")
|
|
End Function
|
|
|
|
Sub ShowAdvancedScriptInfo(script As VideoScript)
|
|
script.Synchronize()
|
|
|
|
If Not script.Path.FileExists Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If script.Engine = ScriptEngine.AviSynth Then
|
|
Using td As New TaskDialog(Of String)
|
|
td.Title = "Choose below"
|
|
td.AddCommand("Info()")
|
|
td.AddCommand("avsmeter benchmark")
|
|
td.AddCommand("avsmeter info")
|
|
td.AddCommand("avs2pipemod info")
|
|
|
|
Select Case td.Show()
|
|
Case "avs2pipemod info"
|
|
g.RunCodeInTerminal($"""`n{Package.avs2pipemod.Name} {Package.avs2pipemod.Version}""; & '{Package.avs2pipemod.Path}' -dll=""{Package.AviSynth.Path.Escape}"" -info '{script.Path}'")
|
|
Case "avsmeter benchmark"
|
|
g.RunCodeInTerminal($"& '{Package.AVSMeter.Path}' '{script.Path.ToShortFilePath}'")
|
|
Case "avsmeter info"
|
|
g.RunCodeInTerminal($"& '{Package.AVSMeter.Path}' -info '{script.Path.ToShortFilePath}';""""")
|
|
Case "Info()"
|
|
Dim infoScript = New VideoScript
|
|
infoScript.AddFilter(New VideoFilter($"Import(""{script.Path}"")"))
|
|
Dim infoCode = $"Info(size={(script.GetInfo().Height * 0.05).ToInvariantString()})"
|
|
infoScript.AddFilter(New VideoFilter(infoCode))
|
|
infoScript.Path = p.TempDir + p.TargetFile.Base + $"_info." + script.FileType
|
|
|
|
If infoScript.GetError() <> "" Then
|
|
MsgError("Script Error", infoScript.GetError())
|
|
Exit Sub
|
|
End If
|
|
|
|
g.PlayScriptWithMPV(infoScript, "--pause=yes --osc=no --osd-level=0")
|
|
End Select
|
|
End Using
|
|
Else
|
|
Using td As New TaskDialog(Of String)
|
|
td.Title = "Choose below"
|
|
td.AddCommand("ClipInfo()")
|
|
td.AddCommand("vspipe info")
|
|
|
|
Select Case td.Show()
|
|
Case "vspipe info"
|
|
g.RunCodeInTerminal($"""`n{Package.vspipe.Name} {Package.vspipe.Version}`n""; & '{Package.vspipe.Path}' --info '{script.Path}' -;""""")
|
|
Case "ClipInfo()"
|
|
Dim infoScript = script.GetNewScript
|
|
infoScript.Path = p.TempDir + p.TargetFile.Base + "_info." + script.FileType
|
|
infoScript.AddFilter(New VideoFilter("clip = clip.resize.Bicubic(720, (720 / clip.width * clip.height) // 8 * 8)"))
|
|
infoScript.AddFilter(New VideoFilter("clip = core.text.ClipInfo(clip)"))
|
|
|
|
If infoScript.GetError() <> "" Then
|
|
MsgError("Script Error", infoScript.GetError())
|
|
Exit Sub
|
|
End If
|
|
|
|
g.PlayScriptWithMPV(infoScript, "--pause=yes --osc=no --osd-level=0")
|
|
End Select
|
|
End Using
|
|
End If
|
|
End Sub
|
|
|
|
Function IsDevelopmentPC() As Boolean
|
|
Return Application.StartupPath.EndsWith("\bin") OrElse Application.StartupPath.EndsWith("\bin-x86")
|
|
End Function
|
|
|
|
Sub RunTask(action As Action)
|
|
Task.Run(Sub()
|
|
Try
|
|
action.Invoke
|
|
Catch ex As Exception
|
|
OnException(ex)
|
|
End Try
|
|
End Sub)
|
|
End Sub
|
|
|
|
Function RunSTATask(action As Action) As Task
|
|
Dim tcs = New TaskCompletionSource(Of Object)()
|
|
Dim thread = New Thread(Sub()
|
|
Try
|
|
action?.Invoke()
|
|
tcs.SetResult(Nothing)
|
|
Catch ex As Exception
|
|
tcs.SetException(ex)
|
|
End Try
|
|
End Sub)
|
|
thread.SetApartmentState(ApartmentState.STA)
|
|
thread.Start()
|
|
Return tcs.Task
|
|
End Function
|
|
|
|
Sub UpdateTrim(script As VideoScript, Optional moveToTheEnd As Boolean = False)
|
|
If script Is Nothing Then Exit Sub
|
|
If moveToTheEnd OrElse Not p.Ranges.Any() Then script.RemoveFilter("Cutting")
|
|
|
|
p.RangesBasedOnFPS = script.OriginalInfo.FrameRate
|
|
Dim filter As VideoFilter = script.GetFilter("Cutting")
|
|
|
|
If p.Ranges.Any() Then
|
|
If filter Is Nothing Then
|
|
filter = New VideoFilter()
|
|
script.Filters.Add(filter)
|
|
End If
|
|
filter.Path = "Cutting"
|
|
filter.Category = "Cutting"
|
|
filter.Script = GetTrim(script)
|
|
filter.Active = True
|
|
End If
|
|
End Sub
|
|
|
|
Function GetTrim(script As VideoScript) As String
|
|
Dim ret As String
|
|
|
|
For Each i In p.Ranges
|
|
If ret <> "" Then
|
|
ret += " + "
|
|
End If
|
|
|
|
If script.Engine = ScriptEngine.AviSynth Then
|
|
ret += "Trim(" & i.Start & ", " & i.End & ")"
|
|
|
|
If p.TrimCode <> "" Then
|
|
ret += "." + p.TrimCode.TrimStart("."c)
|
|
End If
|
|
Else
|
|
ret += "clip[" & i.Start & ":" & (i.End + 1) & "]"
|
|
End If
|
|
Next
|
|
|
|
If script.Engine = ScriptEngine.AviSynth Then
|
|
Return ret
|
|
Else
|
|
Return "clip = " + ret
|
|
End If
|
|
End Function
|
|
|
|
Function ContainsPipeTool(value As String) As Boolean
|
|
If value <> "" Then
|
|
Return value.Contains("ffmpeg") OrElse value.Contains("avs2pipemod") OrElse value.Contains("vspipe")
|
|
End If
|
|
End Function
|
|
|
|
Function GetFilterProfilesText(categories As List(Of FilterCategory)) As String
|
|
Dim ret = ""
|
|
Dim wasMultiline As Boolean
|
|
|
|
For Each i In categories
|
|
ret += "[" + i.Name + "]" + BR
|
|
|
|
For Each filter In i.Filters
|
|
If filter.Script.Contains(BR) Then
|
|
Dim lines = filter.Script.SplitLinesNoEmpty
|
|
|
|
For x = 0 To lines.Length - 1
|
|
lines(x) = " " + lines(x)
|
|
Next
|
|
|
|
ret += BR + filter.Path + " =" + BR + lines.Join(BR) + BR
|
|
wasMultiline = True
|
|
Else
|
|
If wasMultiline Then
|
|
ret += BR
|
|
End If
|
|
|
|
ret += filter.Path + " = " + filter.Script + BR
|
|
wasMultiline = False
|
|
End If
|
|
Next
|
|
|
|
If Not ret.EndsWith(BR2) Then
|
|
ret += BR
|
|
End If
|
|
Next
|
|
|
|
Return ret
|
|
End Function
|
|
|
|
Function GetCodeFont(Optional size As Integer = 10) As Font
|
|
Return New Font(If(s.CodeFont = "", "Consolas", s.CodeFont), size * s.UIScaleFactor)
|
|
End Function
|
|
End Class
|