Port to .NET 5

This commit is contained in:
Dankrushen
2021-01-26 02:37:16 -05:00
parent 0a236361e0
commit 72dcf90b68
95 changed files with 24769 additions and 1 deletions

442
Code5/.gitignore vendored Normal file
View File

@@ -0,0 +1,442 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*[.json, .xml, .info]
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
##
## Visual studio for Mac
##
# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/
# Mac bundle stuff
*.dmg
*.app
# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# JetBrains Rider
.idea/
*.sln.iml
##
## Visual Studio Code
##
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

View File

@@ -0,0 +1,196 @@
using Flowframes.IO;
using Flowframes.OS;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes
{
class AvProcess
{
public static Process lastProcess;
public enum TaskType { ExtractFrames, Encode, GetInfo, Merge, Other };
public static TaskType lastTask = TaskType.Other;
public static string lastOutputFfmpeg;
public static string lastOutputGifski;
public enum LogMode { Visible, OnlyLastLine, Hidden }
static LogMode currentLogMode;
public static async Task RunFfmpeg(string args, LogMode logMode, TaskType taskType = TaskType.Other)
{
await RunFfmpeg(args, "", logMode, taskType);
}
public static async Task RunFfmpeg(string args, string workingDir, LogMode logMode, TaskType taskType = TaskType.Other)
{
lastOutputFfmpeg = "";
currentLogMode = logMode;
Process ffmpeg = OSUtils.NewProcess(true);
lastProcess = ffmpeg;
lastTask = taskType;
if(!string.IsNullOrWhiteSpace(workingDir))
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {workingDir.Wrap()} & {Path.Combine(GetAvDir(), "ffmpeg.exe").Wrap()} -hide_banner -loglevel warning -y -stats {args}";
else
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffmpeg.exe -hide_banner -loglevel warning -y -stats {args}";
if (logMode != LogMode.Hidden) Logger.Log("Running ffmpeg...", false);
Logger.Log("cmd.exe " + ffmpeg.StartInfo.Arguments, true, false, "ffmpeg");
ffmpeg.OutputDataReceived += new DataReceivedEventHandler(FfmpegOutputHandler);
ffmpeg.ErrorDataReceived += new DataReceivedEventHandler(FfmpegOutputHandler);
ffmpeg.Start();
ffmpeg.BeginOutputReadLine();
ffmpeg.BeginErrorReadLine();
while (!ffmpeg.HasExited)
await Task.Delay(1);
}
static void FfmpegOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (outLine == null || outLine.Data == null)
return;
string line = outLine.Data;
lastOutputFfmpeg = lastOutputFfmpeg + "\n" + line;
bool hidden = currentLogMode == LogMode.Hidden;
bool replaceLastLine = currentLogMode == LogMode.OnlyLastLine;
string trimmedLine = line.Remove("q=-0.0").Remove("size=N/A").Remove("bitrate=N/A").TrimWhitespaces();
Logger.Log(trimmedLine, hidden, replaceLastLine, "ffmpeg");
if(line.Contains("Could not open file"))
Interpolate.Cancel($"FFmpeg Error: {line}");
if (line.Contains("No NVENC capable devices found"))
Interpolate.Cancel($"FFmpeg Error: {line}\nMake sure you have an NVENC-capable Nvidia GPU.");
}
static void FfmpegOutputHandlerSilent (object sendingProcess, DataReceivedEventArgs outLine)
{
if (outLine == null || outLine.Data == null || outLine.Data.Trim().Length < 2)
return;
string line = outLine.Data;
if (!string.IsNullOrWhiteSpace(lastOutputFfmpeg))
lastOutputFfmpeg += "\n";
lastOutputFfmpeg = lastOutputFfmpeg + line;
Logger.Log(line, true, false, "ffmpeg");
}
public static string GetFfmpegOutput (string args)
{
Process ffmpeg = OSUtils.NewProcess(true);
lastProcess = ffmpeg;
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffmpeg.exe -hide_banner -y -stats {args}";
Logger.Log("cmd.exe " + ffmpeg.StartInfo.Arguments, true, false, "ffmpeg");
ffmpeg.Start();
ffmpeg.WaitForExit();
string output = ffmpeg.StandardOutput.ReadToEnd();
string err = ffmpeg.StandardError.ReadToEnd();
if (!string.IsNullOrWhiteSpace(err)) output += "\n" + err;
return output;
}
public static async Task<string> GetFfmpegOutputAsync(string args, bool setBusy = false)
{
if (Program.busy)
setBusy = false;
lastOutputFfmpeg = "";
Process ffmpeg = OSUtils.NewProcess(true);
lastProcess = ffmpeg;
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffmpeg.exe -hide_banner -y -stats {args}";
Logger.Log("cmd.exe " + ffmpeg.StartInfo.Arguments, true, false, "ffmpeg");
if (setBusy)
Program.mainForm.SetWorking(true);
ffmpeg.Start();
ffmpeg.OutputDataReceived += new DataReceivedEventHandler(FfmpegOutputHandlerSilent);
ffmpeg.ErrorDataReceived += new DataReceivedEventHandler(FfmpegOutputHandlerSilent);
ffmpeg.Start();
ffmpeg.BeginOutputReadLine();
ffmpeg.BeginErrorReadLine();
while (!ffmpeg.HasExited)
await Task.Delay(10);
if (setBusy)
Program.mainForm.SetWorking(false);
await Task.Delay(100);
return lastOutputFfmpeg;
}
public static string GetFfprobeOutput (string args)
{
Process ffprobe = OSUtils.NewProcess(true);
ffprobe.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffprobe.exe {args}";
Logger.Log("cmd.exe " + ffprobe.StartInfo.Arguments, true, false, "ffmpeg");
ffprobe.Start();
ffprobe.WaitForExit();
string output = ffprobe.StandardOutput.ReadToEnd();
string err = ffprobe.StandardError.ReadToEnd();
if (!string.IsNullOrWhiteSpace(err)) output += "\n" + err;
return output;
}
public static async Task<string> GetFfprobeOutputAsync(string args)
{
Process ffprobe = OSUtils.NewProcess(true);
ffprobe.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffprobe.exe {args}";
Logger.Log("cmd.exe " + ffprobe.StartInfo.Arguments, true, false, "ffmpeg");
ffprobe.Start();
while (!ffprobe.HasExited)
await Task.Delay(1);
string output = ffprobe.StandardOutput.ReadToEnd();
string err = ffprobe.StandardError.ReadToEnd();
if (!string.IsNullOrWhiteSpace(err)) output += "\n" + err;
return output;
}
public static async Task RunGifski(string args, LogMode logMode)
{
lastOutputGifski = "";
currentLogMode = logMode;
Process gifski = OSUtils.NewProcess(true);
lastProcess = gifski;
gifski.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & gifski.exe {args}";
Logger.Log("Running gifski...");
Logger.Log("cmd.exe " + gifski.StartInfo.Arguments, true, false, "ffmpeg");
gifski.OutputDataReceived += new DataReceivedEventHandler(OutputHandlerGifski);
gifski.ErrorDataReceived += new DataReceivedEventHandler(OutputHandlerGifski);
gifski.Start();
gifski.BeginOutputReadLine();
gifski.BeginErrorReadLine();
while (!gifski.HasExited)
await Task.Delay(100);
Logger.Log("Done running gifski.", true);
}
static void OutputHandlerGifski(object sendingProcess, DataReceivedEventArgs outLine)
{
if (outLine == null || outLine.Data == null)
return;
lastOutputGifski = lastOutputGifski + outLine.Data + "\n";
bool hidden = currentLogMode == LogMode.Hidden;
bool replaceLastLine = currentLogMode == LogMode.OnlyLastLine;
Logger.Log(outLine.Data, hidden, replaceLastLine);
}
static string GetAvDir ()
{
return Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(Packages.audioVideo.fileName));
}
static string GetCmdArg ()
{
return "/C";
}
public static async Task SetBusyWhileRunning ()
{
if (Program.busy) return;
await Task.Delay(100);
while(!lastProcess.HasExited)
await Task.Delay(10);
}
}
}

View File

@@ -0,0 +1,496 @@
using Flowframes.AudioVideo;
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.Main;
using Flowframes.MiscUtils;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Utils = Flowframes.AudioVideo.FFmpegUtils;
namespace Flowframes
{
class FFmpegCommands
{
static string hdrFilter = @"-vf select=gte(n\,%frNum%),zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p";
static string divisionFilter = "\"crop=trunc(iw/2)*2:trunc(ih/2)*2\"";
static string pngComprArg = "-compression_level 3";
static string mpDecDef = "\"mpdecimate\"";
static string mpDecAggr = "\"mpdecimate=hi=64*32:lo=64*32:frac=0.1\"";
public static async Task ExtractSceneChanges(string inputFile, string frameFolderPath, float rate)
{
Logger.Log("Extracting scene changes...");
await VideoToFrames(inputFile, frameFolderPath, false, rate, false, false, new Size(320, 180), false, true);
bool hiddenLog = Interpolate.currentInputFrameCount <= 50;
int amount = IOUtils.GetAmountOfFiles(frameFolderPath, false);
Logger.Log($"Detected {amount} scene {(amount == 1 ? "change" : "changes")}.".Replace(" 0 ", " no "), false, !hiddenLog);
}
public static async Task VideoToFrames(string inputFile, string framesDir, bool alpha, float rate, bool deDupe, bool delSrc, bool timecodes = true)
{
await VideoToFrames(inputFile, framesDir, alpha, rate, deDupe, delSrc, new Size(), timecodes);
}
public static async Task VideoToFrames(string inputFile, string framesDir, bool alpha, float rate, bool deDupe, bool delSrc, Size size, bool timecodes, bool sceneDetect = false)
{
if (!sceneDetect) Logger.Log("Extracting video frames from input video...");
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
IOUtils.CreateDir(framesDir);
string timecodeStr = timecodes ? $"-copyts -r {FrameOrder.timebase} -frame_pts true" : "-copyts -frame_pts true";
string scnDetect = sceneDetect ? $"\"select='gt(scene,{Config.GetFloatString("scnDetectValue")})'\"" : "";
string mpStr = deDupe ? ((Config.GetInt("mpdecimateMode") == 0) ? mpDecDef : mpDecAggr) : "";
string filters = FormatUtils.ConcatStrings(new string[] { scnDetect, mpStr } );
string vf = filters.Length > 2 ? $"-vf {filters}" : "";
string rateArg = (rate > 0) ? $" -r {rate.ToStringDot()}" : "";
string pixFmt = alpha ? "-pix_fmt rgba" : "-pix_fmt rgb24"; // Use RGBA for GIF for alpha support
string args = $"{rateArg} -i {inputFile.Wrap()} {pngComprArg} -vsync 0 {pixFmt} {timecodeStr} {vf} {sizeStr} \"{framesDir}/%{Padding.inputFrames}d.png\"";
AvProcess.LogMode logMode = Interpolate.currentInputFrameCount > 50 ? AvProcess.LogMode.OnlyLastLine : AvProcess.LogMode.Hidden;
await AvProcess.RunFfmpeg(args, logMode, AvProcess.TaskType.ExtractFrames);
int amount = IOUtils.GetAmountOfFiles(framesDir, false, "*.png");
if (!sceneDetect) Logger.Log($"Extracted {amount} {(amount == 1 ? "frame" : "frames")} from input.", false, true);
await Task.Delay(1);
if (delSrc)
DeleteSource(inputFile);
}
public static async Task ImportImages(string inpath, string outpath, bool alpha, Size size, bool delSrc = false, bool showLog = true)
{
if (showLog) Logger.Log("Importing images...");
Logger.Log($"Importing images from {inpath} to {outpath}.");
IOUtils.CreateDir(outpath);
string concatFile = Path.Combine(Paths.GetDataPath(), "png-concat-temp.ini");
string concatFileContent = "";
string[] files = IOUtils.GetFilesSorted(inpath);
foreach (string img in files)
concatFileContent += $"file '{img.Replace(@"\", "/")}'\n";
File.WriteAllText(concatFile, concatFileContent);
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
string pixFmt = alpha ? "-pix_fmt rgba" : "-pix_fmt rgb24"; // Use RGBA for GIF for alpha support
string args = $" -loglevel panic -f concat -safe 0 -i {concatFile.Wrap()} {pngComprArg} {sizeStr} {pixFmt} -vsync 0 -vf {divisionFilter} \"{outpath}/%{Padding.inputFrames}d.png\"";
AvProcess.LogMode logMode = IOUtils.GetAmountOfFiles(inpath, false) > 50 ? AvProcess.LogMode.OnlyLastLine : AvProcess.LogMode.Hidden;
await AvProcess.RunFfmpeg(args, logMode, AvProcess.TaskType.ExtractFrames);
if (delSrc)
DeleteSource(inpath);
}
public static async Task ImportSingleImage(string inputFile, string outPath, Size size)
{
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
bool isPng = (Path.GetExtension(outPath).ToLower() == ".png");
string comprArg = isPng ? pngComprArg : "";
string pixFmt = "-pix_fmt " + (isPng ? $"rgb24 {comprArg}" : "yuvj420p");
string args = $"-i {inputFile.Wrap()} {comprArg} {sizeStr} {pixFmt} -vf {divisionFilter} {outPath.Wrap()}";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden, AvProcess.TaskType.ExtractFrames);
}
public static async Task ExtractSingleFrame(string inputFile, string outputPath, int frameNum)
{
bool isPng = (Path.GetExtension(outputPath).ToLower() == ".png");
string comprArg = isPng ? pngComprArg : "";
string pixFmt = "-pix_fmt " + (isPng ? $"rgb24 {comprArg}" : "yuvj420p");
string args = $"-i {inputFile.Wrap()} -vf \"select=eq(n\\,{frameNum})\" -vframes 1 {pixFmt} {outputPath.Wrap()}";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden, AvProcess.TaskType.ExtractFrames);
}
public static async Task ExtractLastFrame(string inputFile, string outputPath, Size size)
{
if (IOUtils.IsPathDirectory(outputPath))
outputPath = Path.Combine(outputPath, "last.png");
bool isPng = (Path.GetExtension(outputPath).ToLower() == ".png");
string comprArg = isPng ? pngComprArg : "";
string pixFmt = "-pix_fmt " + (isPng ? $"rgb24 {comprArg}" : "yuvj420p");
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
string args = $"-sseof -1 -i {inputFile.Wrap()} -update 1 {pixFmt} {sizeStr} {outputPath.Wrap()}";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden, AvProcess.TaskType.ExtractFrames);
}
public static async Task FramesToVideoConcat(string framesFile, string outPath, Interpolate.OutMode outMode, float fps, AvProcess.LogMode logMode = AvProcess.LogMode.OnlyLastLine, bool isChunk = false)
{
await FramesToVideoConcat(framesFile, outPath, outMode, fps, 0, logMode, isChunk);
}
public static async Task FramesToVideoConcat(string framesFile, string outPath, Interpolate.OutMode outMode, float fps, float resampleFps, AvProcess.LogMode logMode = AvProcess.LogMode.OnlyLastLine, bool isChunk = false)
{
if (logMode != AvProcess.LogMode.Hidden)
Logger.Log((resampleFps <= 0) ? $"Encoding video..." : $"Encoding video resampled to {resampleFps.ToString().Replace(",", ".")} FPS...");
Directory.CreateDirectory(outPath.GetParentDir());
string encArgs = Utils.GetEncArgs(Utils.GetCodec(outMode));
if (!isChunk) encArgs += $" -movflags +faststart";
string vfrFilename = Path.GetFileName(framesFile);
string rate = fps.ToString().Replace(",", ".");
string vf = (resampleFps <= 0) ? "" : $"-vf fps=fps={resampleFps.ToStringDot()}";
string extraArgs = Config.Get("ffEncArgs");
string args = $"-loglevel error -vsync 0 -f concat -r {rate} -i {vfrFilename} {encArgs} {vf} {extraArgs} -threads {Config.GetInt("ffEncThreads")} {outPath.Wrap()}";
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), logMode, AvProcess.TaskType.Encode);
}
public static async Task ConcatVideos(string concatFile, string outPath, int looptimes = -1)
{
Logger.Log($"Merging videos...", false, Logger.GetLastLine().Contains("frame"));
string loopStr = (looptimes > 0) ? $"-stream_loop {looptimes}" : "";
string vfrFilename = Path.GetFileName(concatFile);
string args = $" {loopStr} -vsync 1 -f concat -i {vfrFilename} -c copy -movflags +faststart {outPath.Wrap()}";
await AvProcess.RunFfmpeg(args, concatFile.GetParentDir(), AvProcess.LogMode.Hidden, AvProcess.TaskType.Merge);
}
public static async Task FramesToGifConcat(string framesFile, string outPath, float fps, bool palette, int colors = 64, float resampleFps = -1, AvProcess.LogMode logMode = AvProcess.LogMode.OnlyLastLine)
{
if (logMode != AvProcess.LogMode.Hidden)
Logger.Log((resampleFps <= 0) ? $"Encoding GIF..." : $"Encoding GIF resampled to {resampleFps.ToString().Replace(",", ".")} FPS...");
string vfrFilename = Path.GetFileName(framesFile);
string paletteFilter = palette ? $"-vf \"split[s0][s1];[s0]palettegen={colors}[p];[s1][p]paletteuse=dither=floyd_steinberg:diff_mode=rectangle\"" : "";
string fpsFilter = (resampleFps <= 0) ? "" : $"fps=fps={resampleFps.ToStringDot()}";
string vf = FormatUtils.ConcatStrings(new string[] { paletteFilter, fpsFilter });
string rate = fps.ToStringDot();
string args = $"-loglevel error -f concat -r {rate} -i {vfrFilename.Wrap()} -f gif {vf} {outPath.Wrap()}";
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), AvProcess.LogMode.OnlyLastLine, AvProcess.TaskType.Encode);
}
public static async Task MergeAlphaIntoRgb (string rgbDir, int rgbPad, string alphaDir, int aPad)
{
string filter = "-filter_complex [0:v:0][1:v:0]alphamerge[out] -map [out]";
string args = $"-i \"{rgbDir}/%{rgbPad}d.png\" -i \"{alphaDir}/%{aPad}d.png\" {filter} \"{rgbDir}/%{rgbPad}d.png\"";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden);
}
public static async Task LoopVideo(string inputFile, int times, bool delSrc = false)
{
string pathNoExt = Path.ChangeExtension(inputFile, null);
string ext = Path.GetExtension(inputFile);
string args = $" -stream_loop {times} -i {inputFile.Wrap()} -c copy \"{pathNoExt}-Loop{times}{ext}\"";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden);
if (delSrc)
DeleteSource(inputFile);
}
public static async Task ChangeSpeed(string inputFile, float newSpeedPercent, bool delSrc = false)
{
string pathNoExt = Path.ChangeExtension(inputFile, null);
string ext = Path.GetExtension(inputFile);
float val = newSpeedPercent / 100f;
string speedVal = (1f / val).ToString("0.0000").Replace(",", ".");
string args = " -itsscale " + speedVal + " -i \"" + inputFile + "\" -c copy \"" + pathNoExt + "-" + newSpeedPercent + "pcSpeed" + ext + "\"";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
if (delSrc)
DeleteSource(inputFile);
}
public static async Task Encode(string inputFile, string vcodec, string acodec, int crf, int audioKbps = 0, bool delSrc = false)
{
string outPath = Path.ChangeExtension(inputFile, null) + "-convert.mp4";
string args = $" -i {inputFile.Wrap()} -c:v {vcodec} -crf {crf} -pix_fmt yuv420p -c:a {acodec} -b:a {audioKbps}k -vf {divisionFilter} {outPath.Wrap()}";
if (string.IsNullOrWhiteSpace(acodec))
args = args.Replace("-c:a", "-an");
if (audioKbps < 0)
args = args.Replace($" -b:a {audioKbps}", "");
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine, AvProcess.TaskType.Encode);
if (delSrc)
DeleteSource(inputFile);
}
public static async Task ExtractAudio(string inputFile, string outFile) // https://stackoverflow.com/a/27413824/14274419
{
string audioExt = Utils.GetAudioExt(inputFile);
outFile = Path.ChangeExtension(outFile, audioExt);
Logger.Log($"[FFCmds] Extracting audio from {inputFile} to {outFile}", true);
string args = $" -loglevel panic -i {inputFile.Wrap()} -vn -c:a copy {outFile.Wrap()}";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden);
if (File.Exists(outFile) && IOUtils.GetFilesize(outFile) < 512)
{
Logger.Log("Failed to extract audio losslessly! Trying to re-encode.");
File.Delete(outFile);
outFile = Path.ChangeExtension(outFile, Utils.GetAudioExtForContainer(Path.GetExtension(inputFile)));
args = $" -loglevel panic -i {inputFile.Wrap()} -vn {Utils.GetAudioFallbackArgs(Path.GetExtension(inputFile))} {outFile.Wrap()}";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden);
if ((File.Exists(outFile) && IOUtils.GetFilesize(outFile) < 512) || AvProcess.lastOutputFfmpeg.Contains("Invalid data"))
{
Logger.Log("Failed to extract audio, even with re-encoding. Output will not have audio.");
IOUtils.TryDeleteIfExists(outFile);
return;
}
Logger.Log($"Source audio has been re-encoded as it can't be extracted losslessly. This may decrease the quality slightly.", false, true);
}
}
public static async Task ExtractSubtitles (string inputFile, string outFolder, Interpolate.OutMode outMode)
{
Dictionary<int, string> subDict = await GetSubtitleTracks(inputFile);
foreach (KeyValuePair<int, string> subTrack in subDict)
{
string trackName = subTrack.Value.Length > 4 ? CultureInfo.CurrentCulture.TextInfo.ToTitleCase(subTrack.Value.ToLower()) : subTrack.Value.ToUpper();
string outPath = Path.Combine(outFolder, $"{subTrack.Key}-{trackName}.srt");
string args = $" -loglevel error -i {inputFile.Wrap()} -map 0:s:{subTrack.Key} {outPath.Wrap()}";
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden);
if (AvProcess.lastOutputFfmpeg.Contains("matches no streams")) // Break if there are no more subtitle tracks
break;
Logger.Log($"[FFCmds] Extracted subtitle track {subTrack.Key} to {outPath} ({FormatUtils.Bytes(IOUtils.GetFilesize(outPath))})", true, false, "ffmpeg");
}
if(subDict.Count > 0)
{
Logger.Log($"Extracted {subDict.Count} subtitle tracks from the input video.");
Utils.ContainerSupportsSubs(Utils.GetExt(outMode), true);
}
}
public static async Task<Dictionary<int, string>> GetSubtitleTracks (string inputFile)
{
Dictionary<int, string> subDict = new Dictionary<int, string>();
string args = $"-i {inputFile.Wrap()}";
string[] outputLines = (await AvProcess.GetFfmpegOutputAsync(args)).SplitIntoLines();
string[] filteredLines = outputLines.Where(l => l.Contains(" Subtitle: ")).ToArray();
int idx = 0;
foreach(string line in filteredLines)
{
string lang = "unknown";
bool hasLangInfo = line.Contains("(") && line.Contains("): Subtitle: ");
if (hasLangInfo)
lang = line.Split('(')[1].Split(')')[0];
subDict.Add(idx, lang);
idx++;
}
return subDict;
}
public static async Task MergeAudioAndSubs(string inputFile, string audioPath, string tempFolder, int looptimes = -1) // https://superuser.com/a/277667
{
Logger.Log($"[FFCmds] Merging audio from {audioPath} into {inputFile}", true);
string containerExt = Path.GetExtension(inputFile);
string tempPath = Path.Combine(tempFolder, $"vid{containerExt}"); // inputFile + "-temp" + Path.GetExtension(inputFile);
string outPath = Path.Combine(tempFolder, $"muxed{containerExt}"); // inputFile + "-temp" + Path.GetExtension(inputFile);
File.Move(inputFile, tempPath);
string inName = Path.GetFileName(tempPath);
string audioName = Path.GetFileName(audioPath);
string outName = Path.GetFileName(outPath);
bool subs = Utils.ContainerSupportsSubs(containerExt, false) && Config.GetBool("keepSubs");
string subInputArgs = "";
string subMapArgs = "";
string subMetaArgs = "";
string[] subTracks = subs ? IOUtils.GetFilesSorted(tempFolder, false, "*.srt") : new string[0];
for (int subTrack = 0; subTrack < subTracks.Length; subTrack++)
{
subInputArgs += $" -i {Path.GetFileName(subTracks[subTrack])}";
subMapArgs += $" -map {subTrack+2}";
subMetaArgs += $" -metadata:s:s:{subTrack} language={Path.GetFileNameWithoutExtension(subTracks[subTrack]).Split('-').Last()}";
}
string subCodec = Utils.GetSubCodecForContainer(containerExt);
string args = $" -i {inName} -stream_loop {looptimes} -i {audioName.Wrap()}" +
$"{subInputArgs} -map 0:v -map 1:a {subMapArgs} -c:v copy -c:a copy -c:s {subCodec} {subMetaArgs} -shortest {outName}";
await AvProcess.RunFfmpeg(args, tempFolder, AvProcess.LogMode.Hidden);
if ((File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 1024) || AvProcess.lastOutputFfmpeg.Contains("Invalid data") || AvProcess.lastOutputFfmpeg.Contains("Error initializing output stream"))
{
Logger.Log("Failed to merge audio losslessly! Trying to re-encode.", false, false, "ffmpeg");
args = $" -i {inName} -stream_loop {looptimes} -i {audioName.Wrap()}" +
$"{subInputArgs} -map 0:v -map 1:a {subMapArgs} -c:v copy {Utils.GetAudioFallbackArgs(Path.GetExtension(inputFile))} -c:s {subCodec} {subMetaArgs} -shortest {outName}";
await AvProcess.RunFfmpeg(args, tempFolder, AvProcess.LogMode.Hidden);
if ((File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 1024) || AvProcess.lastOutputFfmpeg.Contains("Invalid data") || AvProcess.lastOutputFfmpeg.Contains("Error initializing output stream"))
{
Logger.Log("Failed to merge audio, even with re-encoding. Output will not have audio.", false, false, "ffmpeg");
IOUtils.TryDeleteIfExists(tempPath);
return;
}
string audioExt = Path.GetExtension(audioPath).Remove(".").ToUpper();
Logger.Log($"Source audio ({audioExt}) has been re-encoded to fit into the target container ({containerExt.Remove(".").ToUpper()}). This may decrease the quality slightly.", false, true, "ffmpeg");
}
//string movePath = Path.ChangeExtension(inputFile, Path.GetExtension(tempPath));
//File.Delete(movePath);
if(File.Exists(outPath) && IOUtils.GetFilesize(outPath) > 512)
{
File.Delete(tempPath);
File.Move(outPath, inputFile);
}
else
{
File.Move(tempPath, inputFile);
}
}
public static float GetFramerate(string inputFile)
{
Logger.Log("Reading FPS using ffmpeg.", true, false, "ffmpeg");
string args = $" -i {inputFile.Wrap()}";
string output = AvProcess.GetFfmpegOutput(args);
string[] entries = output.Split(',');
foreach (string entry in entries)
{
if (entry.Contains(" fps") && !entry.Contains("Input ")) // Avoid reading FPS from the filename, in case filename contains "fps"
{
Logger.Log("[FFCmds] FPS Entry: " + entry, true);
string num = entry.Replace(" fps", "").Trim().Replace(",", ".");
float value;
float.TryParse(num, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
return value;
}
}
return 0f;
}
public static Size GetSize(string inputFile)
{
string args = $" -v panic -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 {inputFile.Wrap()}";
string output = AvProcess.GetFfprobeOutput(args);
if (output.Length > 4 && output.Contains("x"))
{
string[] numbers = output.Split('x');
return new Size(numbers[0].GetInt(), numbers[1].GetInt());
}
return new Size(0, 0);
}
public static int GetFrameCount(string inputFile)
{
int frames = 0;
Logger.Log("Reading frame count using ffprobe.", true, false, "ffmpeg");
frames = ReadFrameCountFfprobe(inputFile, Config.GetBool("ffprobeCountFrames")); // Try reading frame count with ffprobe
if (frames > 0)
return frames;
Logger.Log($"Failed to get frame count using ffprobe (frames = {frames}). Reading frame count using ffmpeg.", true, false, "ffmpeg");
frames = ReadFrameCountFfmpeg(inputFile); // Try reading frame count with ffmpeg
if (frames > 0)
return frames;
Logger.Log("Failed to get total frame count of video.");
return 0;
}
public static async Task<int> GetFrameCountAsync(string inputFile)
{
int frames = 0;
Logger.Log("Reading frame count using ffprobe.", true, false, "ffmpeg");
frames = await ReadFrameCountFfprobeAsync(inputFile, Config.GetBool("ffprobeCountFrames")); // Try reading frame count with ffprobe
if (frames > 0)
return frames;
Logger.Log($"Failed to get frame count using ffprobe (frames = {frames}). Reading frame count using ffmpeg.", true, false, "ffmpeg");
frames = await ReadFrameCountFfmpegAsync(inputFile); // Try reading frame count with ffmpeg
if (frames > 0)
return frames;
Logger.Log("Failed to get total frame count of video.");
return 0;
}
static int ReadFrameCountFfprobe(string inputFile, bool readFramesSlow)
{
string args = $" -v panic -select_streams v:0 -show_entries stream=nb_frames -of default=noprint_wrappers=1 {inputFile.Wrap()}";
if (readFramesSlow)
{
Logger.Log("Counting total frames using FFprobe. This can take a moment...");
args = $" -v panic -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 {inputFile.Wrap()}";
}
string info = AvProcess.GetFfprobeOutput(args);
string[] entries = info.SplitIntoLines();
try
{
if (readFramesSlow)
return info.GetInt();
foreach (string entry in entries)
{
if (entry.Contains("nb_frames="))
return entry.GetInt();
}
}
catch { }
return -1;
}
static async Task<int> ReadFrameCountFfprobeAsync(string inputFile, bool readFramesSlow)
{
string args = $" -v panic -select_streams v:0 -show_entries stream=nb_frames -of default=noprint_wrappers=1 {inputFile.Wrap()}";
if (readFramesSlow)
{
Logger.Log("Counting total frames using FFprobe. This can take a moment...");
await Task.Delay(10);
args = $" -v panic -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 {inputFile.Wrap()}";
}
string info = AvProcess.GetFfprobeOutput(args);
string[] entries = info.SplitIntoLines();
try
{
if (readFramesSlow)
return info.GetInt();
foreach (string entry in entries)
{
if (entry.Contains("nb_frames="))
return entry.GetInt();
}
}
catch { }
return -1;
}
static int ReadFrameCountFfmpeg(string inputFile)
{
string args = $" -loglevel panic -i {inputFile.Wrap()} -map 0:v:0 -c copy -f null - ";
string info = AvProcess.GetFfmpegOutput(args);
string[] entries = info.SplitIntoLines();
foreach (string entry in entries)
{
if (entry.Contains("frame="))
return entry.Substring(0, entry.IndexOf("fps")).GetInt();
}
return -1;
}
static async Task<int> ReadFrameCountFfmpegAsync (string inputFile)
{
string args = $" -loglevel panic -i {inputFile.Wrap()} -map 0:v:0 -c copy -f null - ";
string info = await AvProcess.GetFfmpegOutputAsync(args, true);
try
{
string[] lines = info.SplitIntoLines();
string lastLine = lines.Last();
return lastLine.Substring(0, lastLine.IndexOf("fps")).GetInt();
}
catch
{
return -1;
}
}
public static string GetAudioCodec(string path)
{
string args = $" -v panic -show_streams -select_streams a -show_entries stream=codec_name {path.Wrap()}";
string info = AvProcess.GetFfprobeOutput(args);
string[] entries = info.SplitIntoLines();
foreach (string entry in entries)
{
if (entry.Contains("codec_name="))
return entry.Split('=')[1];
}
return "";
}
static void DeleteSource(string path)
{
Logger.Log("[FFCmds] Deleting input file/dir: " + path, true);
if (IOUtils.IsPathDirectory(path) && Directory.Exists(path))
Directory.Delete(path, true);
if (!IOUtils.IsPathDirectory(path) && File.Exists(path))
File.Delete(path);
}
}
}

View File

@@ -0,0 +1,182 @@
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.AudioVideo
{
class FFmpegUtils
{
public enum Codec { H264, H265, NVENC264, NVENC265, VP9, ProRes, AviRaw }
public static Codec GetCodec(Interpolate.OutMode mode)
{
if (mode == Interpolate.OutMode.VidMp4 || mode == Interpolate.OutMode.VidMkv)
{
int mp4Enc = Config.GetInt("mp4Enc");
if (mp4Enc == 0) return Codec.H264;
if (mp4Enc == 1) return Codec.H265;
if (mp4Enc == 2) return Codec.NVENC264;
if (mp4Enc == 3) return Codec.NVENC265;
}
if (mode == Interpolate.OutMode.VidWebm)
return Codec.VP9;
if (mode == Interpolate.OutMode.VidProRes)
return Codec.ProRes;
if (mode == Interpolate.OutMode.VidAvi)
return Codec.AviRaw;
return Codec.H264;
}
public static string GetEnc(Codec codec)
{
switch (codec)
{
case Codec.H264: return "libx264";
case Codec.H265: return "libx265";
case Codec.NVENC264: return "h264_nvenc";
case Codec.NVENC265: return "hevc_nvenc";
case Codec.VP9: return "libvpx-vp9";
case Codec.ProRes: return "prores_ks";
case Codec.AviRaw: return Config.Get("aviCodec");
}
return "libx264";
}
public static string GetEncArgs (Codec codec)
{
string args = $"-c:v { GetEnc(codec)} ";
if(codec == Codec.H264)
{
args += $"-crf {Config.GetInt("h264Crf")} -preset {Config.Get("ffEncPreset")} -pix_fmt yuv420p";
}
if (codec == Codec.H265)
{
args += $"-crf {Config.GetInt("h265Crf")} -preset {Config.Get("ffEncPreset")} -pix_fmt yuv420p";
}
if (codec == Codec.NVENC264)
{
args += $"-crf {Config.GetInt("h264Crf")} -preset default -pix_fmt yuv420p";
}
if (codec == Codec.NVENC265)
{
args += $"-crf {Config.GetInt("h265Crf")} -preset default -pix_fmt yuv420p";
}
if (codec == Codec.VP9)
{
int crf = Config.GetInt("vp9Crf");
string qualityStr = (crf > 0) ? $"-crf {crf}" : "-lossless 1";
string cpuUsed = Config.GetInt("vp9Speed", 3).ToString();
args += $"{qualityStr} -cpu-used {cpuUsed} -tile-columns 2 -tile-rows 2 -row-mt 1 -pix_fmt yuv420p";
}
if(codec == Codec.ProRes)
{
args += $"-profile:v {Config.GetInt("proResProfile")} -pix_fmt yuv420p";
}
if (codec == Codec.AviRaw)
{
args += $"-pix_fmt {Config.Get("aviColors")}";
}
return args;
}
public static string GetAudioEnc(Codec codec)
{
switch (codec)
{
case Codec.VP9: return "libopus";
}
return "aac";
}
public static string GetExt(Interpolate.OutMode outMode, bool dot = true)
{
string ext = dot ? "." : "";
switch (outMode)
{
case Interpolate.OutMode.VidMp4: ext += "mp4"; break;
case Interpolate.OutMode.VidMkv: ext += "mkv"; break;
case Interpolate.OutMode.VidWebm: ext += "webm"; break;
case Interpolate.OutMode.VidProRes: ext += "mov"; break;
case Interpolate.OutMode.VidAvi: ext += "avi"; break;
case Interpolate.OutMode.VidGif: ext += "gif"; break;
}
return ext;
}
public static string GetAudioExt(string videoFile)
{
switch (FFmpegCommands.GetAudioCodec(videoFile))
{
case "vorbis": return "ogg";
case "opus": return "ogg";
case "mp2": return "mp2";
case "aac": return "m4a";
default: return "wav";
}
}
public static string GetAudioFallbackArgs (string containerExt)
{
containerExt = containerExt.Remove(".");
string codec = "aac";
string bitrate = $"{Config.GetInt("aacBitrate", 160)}k";
if(containerExt == "webm" || containerExt == "mkv")
{
codec = "libopus";
bitrate = $"{Config.GetInt("opusBitrate", 128)}";
}
return $"-c:a {codec} -b:a {bitrate}k -ac 2";
}
public static string GetAudioExtForContainer(string containerExt)
{
containerExt = containerExt.Remove(".");
string ext = "m4a";
if (containerExt == "webm" || containerExt == "mkv")
ext = "ogg";
return ext;
}
public static string GetSubCodecForContainer(string containerExt)
{
containerExt = containerExt.Remove(".");
if (containerExt == "mp4") return "mov_text";
if (containerExt == "webm") return "webvtt";
return "copy"; // Default: Copy SRT subs
}
public static bool ContainerSupportsSubs(string containerExt, bool showWarningIfNotSupported = true)
{
containerExt = containerExt.Remove(".");
bool supported = (containerExt == "mp4" || containerExt == "mkv" || containerExt == "webm" || containerExt == "mov");
Logger.Log($"Subtitles {(supported ? "are supported" : "not supported")} by {containerExt.ToUpper()}", true);
if (Config.GetBool("keepSubs") && !supported)
Logger.Log($"Warning: Subtitles are enabled, but {containerExt.ToUpper()} does not support them. MKV is recommended instead.");
return supported;
}
}
}

30
Code5/Data/AI.cs Normal file
View File

@@ -0,0 +1,30 @@
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Data
{
public struct AI
{
public string aiName;
public string aiNameShort;
public string friendlyName;
public string description;
public FlowPackage pkg;
public bool supportsAnyExp;
public AI(string aiNameArg, string friendlyNameArg, string descArg, FlowPackage pkgArg, bool supportsAnyExpArg)
{
aiName = aiNameArg;
aiNameShort = aiNameArg.Split(' ')[0];
aiNameShort = aiNameArg.Split('_')[0];
friendlyName = friendlyNameArg;
description = descArg;
pkg = pkgArg;
supportsAnyExp = supportsAnyExpArg;
}
}
}

24
Code5/Data/FlowPackage.cs Normal file
View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.IO
{
public struct FlowPackage
{
public string friendlyName;
public string fileName;
public int downloadSizeMb;
public string desc;
public FlowPackage(string friendlyNameStr, string fileNameStr, int downloadSizeMbInt, string description)
{
friendlyName = friendlyNameStr;
fileName = fileNameStr;
downloadSizeMb = downloadSizeMbInt;
desc = description;
}
}
}

10
Code5/Data/Formats.cs Normal file
View File

@@ -0,0 +1,10 @@

namespace Flowframes.IO
{
class Formats
{
public static string[] supported = { ".mp4", ".m4v", ".gif", ".mkv", ".mpg", ".webm", ".avi", ".wmv", ".ts", ".bik" }; // Supported formats
public static string[] noEncodeSupport = { ".bik" }; // Files that have no encode support, but decode
}
}

View File

@@ -0,0 +1,114 @@
using Flowframes.AudioVideo;
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.Main;
using Flowframes.UI;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
namespace Flowframes
{
public struct InterpSettings
{
public string inPath;
public string outPath;
public AI ai;
public float inFps;
public float outFps;
public int interpFactor;
public Interpolate.OutMode outMode;
public string model;
public string tempFolder;
public string framesFolder;
public string interpFolder;
public bool inputIsFrames;
public string outFilename;
public Size inputResolution;
public Size scaledResolution;
public bool alpha;
public InterpSettings(string inPathArg, string outPathArg, AI aiArg, float inFpsArg, int interpFactorArg, Interpolate.OutMode outModeArg, string modelArg)
{
inPath = inPathArg;
outPath = outPathArg;
ai = aiArg;
inFps = inFpsArg;
interpFactor = interpFactorArg;
outFps = inFpsArg * interpFactorArg;
outMode = outModeArg;
model = modelArg;
alpha = false;
try
{
tempFolder = InterpolateUtils.GetTempFolderLoc(inPath, outPath);
framesFolder = Path.Combine(tempFolder, Paths.framesDir);
interpFolder = Path.Combine(tempFolder, Paths.interpDir);
inputIsFrames = IOUtils.IsPathDirectory(inPath);
outFilename = Path.Combine(outPath, Path.GetFileNameWithoutExtension(inPath) + IOUtils.GetExportSuffix(interpFactor, ai, model) + FFmpegUtils.GetExt(outMode));
}
catch
{
Logger.Log("Tried to create InterpSettings struct without an inpath. Can't set tempFolder, framesFolder and interpFolder.", true);
tempFolder = "";
framesFolder = "";
interpFolder = "";
inputIsFrames = false;
outFilename = "";
}
inputResolution = new Size(0, 0);
scaledResolution = new Size(0, 0);
}
public void UpdatePaths (string inPathArg, string outPathArg)
{
inPath = inPathArg;
outPath = outPathArg;
tempFolder = InterpolateUtils.GetTempFolderLoc(inPath, outPath);
framesFolder = Path.Combine(tempFolder, Paths.framesDir);
interpFolder = Path.Combine(tempFolder, Paths.interpDir);
inputIsFrames = IOUtils.IsPathDirectory(inPath);
outFilename = Path.Combine(outPath, Path.GetFileNameWithoutExtension(inPath) + IOUtils.GetExportSuffix(interpFactor, ai, model) + FFmpegUtils.GetExt(outMode));
}
public async Task<Size> GetInputRes()
{
await RefreshResolutions();
return inputResolution;
}
public async Task<Size> GetScaledRes()
{
await RefreshResolutions();
return scaledResolution;
}
async Task RefreshResolutions ()
{
if (inputResolution.IsEmpty || scaledResolution.IsEmpty)
{
inputResolution = await IOUtils.GetVideoOrFramesRes(inPath);
scaledResolution = InterpolateUtils.GetOutputResolution(inputResolution, false);
}
}
public int GetTargetFrameCount(string overrideInputDir = "", int overrideFactor = -1)
{
if (framesFolder == null || !Directory.Exists(framesFolder))
return 0;
string framesDir = (!string.IsNullOrWhiteSpace(overrideInputDir)) ? overrideInputDir : framesFolder;
int frames = IOUtils.GetAmountOfFiles(framesDir, false, "*.png");
int factor = (overrideFactor > 0) ? overrideFactor : interpFactor;
int targetFrameCount = (frames * factor) - (interpFactor - 1);
return targetFrameCount;
}
}
}

23
Code5/Data/Networks.cs Normal file
View File

@@ -0,0 +1,23 @@
using Flowframes.IO;
using System;
using System.Collections.Generic;
namespace Flowframes.Data
{
class Networks
{
public static AI rifeCuda = new AI("RIFE_CUDA", "RIFE", "CUDA/Pytorch Implementation of RIFE", Packages.rifeCuda, true);
public static AI rifeNcnn = new AI("RIFE_NCNN", "RIFE (NCNN)", "Vulkan/NCNN Implementation of RIFE", Packages.rifeNcnn, false);
public static AI dainNcnn = new AI("DAIN_NCNN", "DAIN (NCNN)", "Vulkan/NCNN Implementation of DAIN", Packages.dainNcnn, true);
public static List<AI> networks = new List<AI>();
public static void Init ()
{
networks.Clear();
networks.Add(rifeCuda);
networks.Add(rifeNcnn);
networks.Add(dainNcnn);
}
}
}

15
Code5/Data/Packages.cs Normal file
View File

@@ -0,0 +1,15 @@

namespace Flowframes.IO
{
class Packages
{
public static FlowPackage dainNcnn = new FlowPackage("DAIN-NCNN (AMD/Nvidia)", "dain-ncnn.7z", 40, "NCNN/Vulkan implementation of DAIN. Very slow and VRAM-hungry.");
//public static FlowPackage cainNcnn = new FlowPackage("CAIN-NCNN (AMD/Nvidia)", "cain-ncnn.7z", 75, "NCNN/Vulkan implementation of CAIN. About 8x faster than DAIN and very lightweight on VRAM.");
public static FlowPackage rifeCuda = new FlowPackage("RIFE (Nvidia)", "rife-cuda.7z", 50, "Pytorch implementation of RIFE. Very fast (~2x CAIN, >16x DAIN) and not too VRAM-heavy.");
public static FlowPackage rifeNcnn = new FlowPackage("RIFE-NCNN (AMD/Nvidia) [EXPERIMENTAL]", "rife-ncnn.7z", 25, "NCNN/Vulkan implementation of RIFE. Similar speed and VRAM usage as CAIN, but can have better quality.");
//public static FlowPackage python = new FlowPackage("Python Runtime", "py.7z", 640, "Embedded Python runtime including Pytorch and all other dependencies. Install this if you don't have system Python.");
public static FlowPackage audioVideo = new FlowPackage("Audio/Video Tools (Required)", "av.7z", 10, "Utilities for extracting frames, analysing videos, encoding videos and GIFs.");
public static FlowPackage licenses = new FlowPackage("Licenses (Required)", "licenses.7z", 1, "License files for redistributed software.");
}
}

14
Code5/Data/Padding.cs Normal file
View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Data
{
class Padding
{
public const int inputFrames = 9;
public const int interpFrames = 8;
}
}

32
Code5/Data/SemVer.cs Normal file
View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
namespace Flowframes.Data
{
public struct SemVer
{
public int major;
public int minor;
public int patch;
public SemVer(int majorNum, int minorNum, int patchNum)
{
major = majorNum;
minor = minorNum;
patch = patchNum;
}
public SemVer(string versionStr)
{
string[] nums = versionStr.Trim().Split('.');
major = nums[0].GetInt();
minor = nums[1].GetInt();
patch = nums[2].GetInt();
}
public override string ToString ()
{
return $"{major}.{minor}.{patch}";
}
}
}

184
Code5/ExtensionMethods.cs Normal file
View File

@@ -0,0 +1,184 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes
{
public static class ExtensionMethods
{
public static string TrimNumbers(this string s, bool allowDotComma = false)
{
if (!allowDotComma)
s = Regex.Replace(s, "[^0-9]", "");
else
s = Regex.Replace(s, "[^.,0-9]", "");
return s.Trim();
}
public static int GetInt(this TextBox textbox)
{
return GetInt(textbox.Text);
}
public static int GetInt(this ComboBox combobox)
{
return GetInt(combobox.Text);
}
public static int GetInt(this string str)
{
if (str.Length < 1 || str == null)
return 0;
try { return int.Parse(str.TrimNumbers()); }
catch (Exception e)
{
Logger.Log("Failed to parse \"" + str + "\" to int: " + e, true);
return 0;
}
}
public static float GetFloat(this TextBox textbox)
{
return GetFloat(textbox.Text);
}
public static float GetFloat(this ComboBox combobox)
{
return GetFloat(combobox.Text);
}
public static float GetFloat(this string str)
{
if (str.Length < 1 || str == null)
return 0f;
string num = str.TrimNumbers(true).Replace(",", ".");
float value;
float.TryParse(num, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
return value;
}
public static string Wrap(this string path, bool addSpaceFront = false, bool addSpaceEnd = false)
{
string s = "\"" + path + "\"";
if (addSpaceFront)
s = " " + s;
if (addSpaceEnd)
s = s + " ";
return s;
}
public static string GetParentDir(this string path)
{
return Directory.GetParent(path).FullName;
}
public static int RoundToInt(this float f)
{
return (int)Math.Round(f);
}
public static int Clamp(this int i, int min, int max)
{
if (i < min)
i = min;
if (i > max)
i = max;
return i;
}
public static string[] SplitIntoLines(this string str)
{
return Regex.Split(str, "\r\n|\r|\n");
}
public static string Trunc(this string inStr, int maxChars, bool addEllipsis = true)
{
string str = inStr.Length <= maxChars ? inStr : inStr.Substring(0, maxChars);
if(addEllipsis && inStr.Length > maxChars)
str += "…";
return str;
}
public static string StripBadChars(this string str)
{
string outStr = Regex.Replace(str, @"[^\u0020-\u007E]", string.Empty);
outStr = outStr.Remove("(").Remove(")").Remove("[").Remove("]").Remove("{").Remove("}").Remove("%").Remove("'");
return outStr;
}
public static string StripNumbers(this string str)
{
return new string(str.Where(c => c != '-' && (c < '0' || c > '9')).ToArray());
}
public static string Remove(this string str, string stringToRemove)
{
if (str == null || stringToRemove == null)
return str;
return str.Replace(stringToRemove, "");
}
public static string TrimWhitespaces(this string str)
{
if (str == null) return str;
var newString = new StringBuilder();
bool previousIsWhitespace = false;
for (int i = 0; i < str.Length; i++)
{
if (Char.IsWhiteSpace(str[i]))
{
if (previousIsWhitespace)
continue;
previousIsWhitespace = true;
}
else
{
previousIsWhitespace = false;
}
newString.Append(str[i]);
}
return newString.ToString();
}
public static string ReplaceLast (this string str, string stringToReplace, string replaceWith)
{
int place = str.LastIndexOf(stringToReplace);
if (place == -1)
return str;
return str.Remove(place, stringToReplace.Length).Insert(place, replaceWith);
}
public static string[] SplitBy (this string str, string splitBy)
{
return str.Split(new string[] { splitBy }, StringSplitOptions.None);
}
public static string RemoveComments (this string str)
{
return str.Split('#')[0].SplitBy("//")[0];
}
public static string FilenameSuffix(this string path, string suffix)
{
string filename = Path.ChangeExtension(path, null);
string ext = Path.GetExtension(path);
return filename + suffix + ext;
}
public static string ToStringDot (this float f, string format = "")
{
if(string.IsNullOrWhiteSpace(format))
return f.ToString().Replace(",", ".");
else
return f.ToString(format).Replace(",", ".");
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

47
Code5/Flowframes.csproj Normal file
View File

@@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<GenerateAssemblyTitleAttribute>false</GenerateAssemblyTitleAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<ApplicationIcon>flowframesIcoNew.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CircularProgressBar" Version="2.8.0.16" />
<PackageReference Include="Crc32.NET" Version="1.2.0" />
<PackageReference Include="CyotekTabList" Version="2.2.0" />
<PackageReference Include="diskdetector-net" Version="0.3.2" />
<PackageReference Include="HTAlt.Standart" Version="0.1.6" />
<PackageReference Include="HTAlt.WinForms" Version="0.1.6" />
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="7.23.1" />
<PackageReference Include="Magick.NET.Core" Version="6.1.0" />
<PackageReference Include="NvAPIWrapper.Net" Version="0.8.1.101" />
<PackageReference Include="Standart.Hash.xxHash" Version="3.1.0" />
<PackageReference Include="System.Management" Version="5.0.0" />
<PackageReference Include="WindowsAPICodePack-Core" Version="1.1.2" />
<PackageReference Include="WindowsAPICodePack-Shell" Version="1.1.1" />
<PackageReference Include="WinFormAnimation" Version="1.6.0.4" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
</Project>

25
Code5/Flowframes.sln Normal file
View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30907.101
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flowframes", "Flowframes.csproj", "{A7E45FC0-5BE5-40FC-BCB6-48096F57AF73}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A7E45FC0-5BE5-40FC-BCB6-48096F57AF73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A7E45FC0-5BE5-40FC-BCB6-48096F57AF73}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7E45FC0-5BE5-40FC-BCB6-48096F57AF73}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7E45FC0-5BE5-40FC-BCB6-48096F57AF73}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {62B981E7-6590-4960-BDF4-3439D25D3840}
EndGlobalSection
EndGlobal

3
Code5/FodyWeavers.xml Normal file
View File

@@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Costura />
</Weavers>

111
Code5/FodyWeavers.xsd Normal file
View File

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="Costura" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:all>
<xs:element minOccurs="0" maxOccurs="1" name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element minOccurs="0" maxOccurs="1" name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with line breaks.</xs:documentation>
</xs:annotation>
</xs:element>
</xs:all>
<xs:attribute name="CreateTemporaryAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeDebugSymbols" type="xs:boolean">
<xs:annotation>
<xs:documentation>Controls if .pdbs for reference assemblies are also embedded.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCompression" type="xs:boolean">
<xs:annotation>
<xs:documentation>Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="DisableCleanup" type="xs:boolean">
<xs:annotation>
<xs:documentation>As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="LoadAtModuleInit" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IgnoreSatelliteAssemblies" type="xs:boolean">
<xs:annotation>
<xs:documentation>Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ExcludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with |</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="IncludeAssemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged32Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 32 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="Unmanaged64Assemblies" type="xs:string">
<xs:annotation>
<xs:documentation>A list of unmanaged 64 bit assembly names to include, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="PreloadOrder" type="xs:string">
<xs:annotation>
<xs:documentation>The order of preloaded assemblies, delimited with |.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

1416
Code5/Form1.Designer.cs generated Normal file

File diff suppressed because it is too large Load Diff

433
Code5/Form1.cs Normal file
View File

@@ -0,0 +1,433 @@
using Flowframes.Forms;
using Flowframes.IO;
using Flowframes.Magick;
using Flowframes.Main;
using Flowframes.OS;
using Flowframes.UI;
using Microsoft.WindowsAPICodePack.Dialogs;
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using HTAlt.WinForms;
using Flowframes.Data;
using Microsoft.WindowsAPICodePack.Taskbar;
using System.Threading.Tasks;
namespace Flowframes
{
public partial class Form1 : Form
{
public bool initialized = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
CheckForIllegalCrossThreadCalls = false;
AutoScaleMode = AutoScaleMode.None;
Text = $"Flowframes {Updater.GetInstalledVer()}";
// Main Tab
UIUtils.InitCombox(interpFactorCombox, 0);
UIUtils.InitCombox(outModeCombox, 0);
UIUtils.InitCombox(aiModel, 2);
// Video Utils
UIUtils.InitCombox(utilsLoopTimesCombox, 0);
UIUtils.InitCombox(utilsSpeedCombox, 0);
UIUtils.InitCombox(utilsConvCrf, 0);
Program.mainForm = this;
Logger.textbox = logBox;
InitAis();
InterpolateUtils.preview = previewPicturebox;
ConfigParser.LoadComboxIndex(aiCombox);
UpdateStepByStepControls(true);
Initialized();
Checks();
}
void Checks()
{
try
{
GetWebInfo.LoadNews(newsLabel);
GetWebInfo.LoadPatronListCsv(patronsLabel);
Updater.AsyncUpdateCheck();
Python.CheckCompression();
}
catch (Exception e)
{
Logger.Log("Non-critical error while performing online checks. See logs for details.");
Logger.Log(e.Message + "\n" + e.StackTrace, true);
}
}
public HTTabControl GetMainTabControl() { return mainTabControl; }
public bool IsInFocus() { return (ActiveForm == this); }
public void SetTab(string tabName)
{
foreach (TabPage tab in mainTabControl.TabPages)
{
if (tab.Text.ToLower() == tabName.ToLower())
mainTabControl.SelectedTab = tab;
}
mainTabControl.Refresh();
mainTabControl.Update();
}
public InterpSettings GetCurrentSettings()
{
SetTab("interpolate");
return new InterpSettings(inputTbox.Text.Trim(), outputTbox.Text.Trim(), GetAi(), fpsInTbox.GetFloat(), interpFactorCombox.GetInt(), GetOutMode(), GetModel());
}
public void LoadBatchEntry(InterpSettings entry)
{
inputTbox.Text = entry.inPath;
outputTbox.Text = entry.outPath;
interpFactorCombox.Text = entry.interpFactor.ToString();
aiCombox.SelectedIndex = Networks.networks.IndexOf(entry.ai);
SetOutMode(entry.outMode);
}
public void SetStatus(string str)
{
Logger.Log(str, true);
statusLabel.Text = str;
}
public void SetProgress(int percent)
{
longProgBar.Value = percent.Clamp(0, 100);
longProgBar.Refresh();
}
public Size currInRes;
public float currInFps;
public int currInFrames;
public void UpdateInputInfo ()
{
string str = $"Resolution: {(!currInRes.IsEmpty ? $"{currInRes.Width}x{currInRes.Height}" : "Unknown")} - ";
str += $"Framerate: {(currInFps > 0f ? $"{currInFps.ToStringDot()} FPS" : "Unknown")} - ";
str += $"Frame Count: {(currInFrames > 0 ? $"{currInFrames} Frames" : "Unknown")}";
inputInfo.Text = str;
}
void InitAis()
{
foreach (AI ai in Networks.networks)
aiCombox.Items.Add(ai.friendlyName + " - " + ai.description);
aiCombox.SelectedIndex = 0;
}
public void Initialized()
{
initialized = true;
runBtn.Enabled = true;
}
private void browseInputBtn_Click(object sender, EventArgs e)
{
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.InitialDirectory = inputTbox.Text.Trim();
dialog.IsFolderPicker = true;
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
DragDropHandler(new string[] { dialog.FileName });
}
private void browseInputFileBtn_Click(object sender, EventArgs e)
{
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.InitialDirectory = inputTbox.Text.Trim();
dialog.IsFolderPicker = false;
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
DragDropHandler(new string[] { dialog.FileName });
}
private void browseOutBtn_Click(object sender, EventArgs e)
{
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.InitialDirectory = inputTbox.Text.Trim();
dialog.IsFolderPicker = true;
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
outputTbox.Text = dialog.FileName;
}
public void runBtn_Click(object sender, EventArgs e)
{
if (!BatchProcessing.busy) // Don't load values from gui if batch processing is used
Interpolate.current = GetCurrentSettings();
Interpolate.Start();
}
public string GetModel()
{
return aiModel.Text.Split('-')[0].Remove(" ").Remove(".");
}
Interpolate.OutMode GetOutMode()
{
Interpolate.OutMode outMode = Interpolate.OutMode.VidMp4;
if (outModeCombox.Text.ToLower().Contains("mkv")) outMode = Interpolate.OutMode.VidMkv;
if (outModeCombox.Text.ToLower().Contains("webm")) outMode = Interpolate.OutMode.VidWebm;
if (outModeCombox.Text.ToLower().Contains("prores")) outMode = Interpolate.OutMode.VidProRes;
if (outModeCombox.Text.ToLower().Contains("avi")) outMode = Interpolate.OutMode.VidAvi;
if (outModeCombox.Text.ToLower().Contains("gif")) outMode = Interpolate.OutMode.VidGif;
if (outModeCombox.Text.ToLower().Contains("image")) outMode = Interpolate.OutMode.ImgPng;
return outMode;
}
public void SetOutMode(Interpolate.OutMode mode)
{
int theIndex = 0;
for(int i = 0; i < outModeCombox.Items.Count; i++)
{
string currentItem = outModeCombox.Items[i].ToString().ToLower();
if (mode == Interpolate.OutMode.VidMkv && currentItem.Contains("mkv")) theIndex = i;
if (mode == Interpolate.OutMode.VidWebm && currentItem.Contains("webm")) theIndex = i;
if (mode == Interpolate.OutMode.VidProRes && currentItem.Contains("prores")) theIndex = i;
if (mode == Interpolate.OutMode.VidAvi && currentItem.Contains("avi")) theIndex = i;
if (mode == Interpolate.OutMode.VidGif && currentItem.Contains("gif")) theIndex = i;
if (mode == Interpolate.OutMode.ImgPng && currentItem.Contains("image")) theIndex = i;
}
outModeCombox.SelectedIndex = theIndex;
}
AI GetAi()
{
return Networks.networks[aiCombox.SelectedIndex];
}
void inputTbox_DragEnter(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Copy; }
private void inputTbox_DragDrop(object sender, DragEventArgs e)
{
DragDropHandler((string[])e.Data.GetData(DataFormats.FileDrop));
}
void outputTbox_DragEnter(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Copy; }
private void outputTbox_DragDrop(object sender, DragEventArgs e)
{
if (Program.busy) return;
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
outputTbox.Text = files[0];
}
private void fpsInTbox_TextChanged(object sender, EventArgs e)
{
fpsInTbox.Text = fpsInTbox.Text.TrimNumbers(true);
//Interpolate.SetFps(fpsInTbox.GetFloat());
UpdateOutputFPS();
}
public void UpdateOutputFPS()
{
float fpsOut = fpsInTbox.GetFloat() * interpFactorCombox.GetFloat();
fpsOutTbox.Text = fpsOut.ToString();
//Interpolate.interpFactor = interpFactorCombox.GetInt();
}
private void interpFactorCombox_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateOutputFPS();
int guiInterpFactor = interpFactorCombox.GetInt();
if (!initialized)
return;
string aiName = GetAi().aiName.Replace("_", "-");
if (!Program.busy && guiInterpFactor > 2 && !GetAi().supportsAnyExp && Config.GetInt("autoEncMode") > 0 && !Logger.GetLastLine().Contains(aiName))
Logger.Log($"Warning: {aiName} doesn't natively support 4x/8x and will run multiple times for {guiInterpFactor}x. Auto-Encode will only work on the last run.");
}
public void SetWorking(bool state, bool allowCancel = true)
{
Logger.Log($"SetWorking({state})", true);
SetProgress(-1);
Control[] controlsToDisable = new Control[] { runBtn, runStepBtn, stepSelector, settingsBtn };
Control[] controlsToHide = new Control[] { runBtn, runStepBtn, stepSelector };
progressCircle.Visible = state;
cancelBtn.Visible = state;
foreach (Control c in controlsToDisable)
c.Enabled = !state;
foreach (Control c in controlsToHide)
c.Visible = !state;
cancelBtn.Enabled = allowCancel;
Program.busy = state;
Program.mainForm.UpdateStepByStepControls(false);
}
string lastAiComboxStr = "";
private void aiCombox_SelectedIndexChanged(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(aiCombox.Text) || aiCombox.Text == lastAiComboxStr) return;
lastAiComboxStr = aiCombox.Text;
aiModel = UIUtils.FillAiModelsCombox(aiModel, GetAi());
interpFactorCombox_SelectedIndexChanged(null, null);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
ConfigParser.SaveComboxIndex(aiCombox);
}
private async void debugExtractFramesBtn_Click(object sender, EventArgs e)
{
await UtilsTab.ExtractVideo(inputTbox.Text.Trim(), utilsExtractAudioCbox.Checked);
}
private void licenseBtn_Click(object sender, EventArgs e)
{
Process.Start("explorer.exe", Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(Packages.licenses.fileName)));
}
private async void utilsLoopVidBtn_Click(object sender, EventArgs e)
{
await UtilsTab.LoopVideo(inputTbox.Text.Trim(), utilsLoopTimesCombox);
}
private async void utilsChangeSpeedBtn_Click(object sender, EventArgs e)
{
await UtilsTab.ChangeSpeed(inputTbox.Text.Trim(), utilsSpeedCombox);
}
private void Form1_DragEnter(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Copy; }
private void Form1_DragDrop(object sender, DragEventArgs e)
{
DragDropHandler((string[])e.Data.GetData(DataFormats.FileDrop));
}
public void DragDropHandler(string[] files)
{
if (Program.busy) return;
if (files.Length > 1)
{
queueBtn_Click(null, null);
if (BatchProcessing.currentBatchForm != null)
BatchProcessing.currentBatchForm.LoadDroppedPaths(files);
}
else
{
SetTab("interpolation");
Logger.Log("Selected video/directory: " + Path.GetFileName(files[0]));
inputTbox.Text = files[0];
MainUiFunctions.InitInput(outputTbox, inputTbox, fpsInTbox);
}
}
private async void utilsConvertMp4Btn_Click(object sender, EventArgs e)
{
await UtilsTab.Convert(inputTbox.Text.Trim(), utilsConvCrf);
}
private void utilsDedupBtn_Click(object sender, EventArgs e)
{
UtilsTab.Dedupe(inputTbox.Text.Trim(), false);
}
private void utilsDedupTestBtn_Click(object sender, EventArgs e)
{
UtilsTab.Dedupe(inputTbox.Text.Trim(), true);
}
private void cancelBtn_Click(object sender, EventArgs e)
{
SetTab("interpolation");
Interpolate.Cancel();
}
private void discordBtn_Click(object sender, EventArgs e)
{
Process.Start("https://discord.gg/eJHD2NSJRe");
}
private void paypalBtn_Click(object sender, EventArgs e)
{
Process.Start("https://www.paypal.com/paypalme/nmkd/10");
}
private void patreonBtn_Click(object sender, EventArgs e)
{
Process.Start("https://patreon.com/n00mkrad");
}
private void settingsBtn_Click(object sender, EventArgs e)
{
new SettingsForm().ShowDialog();
}
private void queueBtn_Click(object sender, EventArgs e)
{
if (BatchProcessing.currentBatchForm != null)
{
BatchProcessing.currentBatchForm.WindowState = FormWindowState.Normal;
BatchProcessing.currentBatchForm.BringToFront();
}
else
{
new BatchForm().Show();
}
}
private void previewPicturebox_MouseClick(object sender, MouseEventArgs e)
{
if (InterpolateUtils.bigPreviewForm == null)
{
InterpolateUtils.bigPreviewForm = new BigPreviewForm();
InterpolateUtils.bigPreviewForm.Show();
InterpolateUtils.bigPreviewForm.SetImage(previewPicturebox.Image);
}
}
private async void updateBtn_Click(object sender, EventArgs e)
{
new UpdaterForm().ShowDialog();
}
private void welcomeLabel2_Click(object sender, EventArgs e)
{
SetTab("interpolation");
}
public void UpdateStepByStepControls(bool settingsMayHaveChanged)
{
if (settingsMayHaveChanged)
{
stepSelector.Items.Clear();
if (Config.GetBool("scnDetect"))
stepSelector.Items.AddRange(new string[] { "1) Extract Scene Changes", "2) Import/Extract Frames", "3) Run Interpolation", "4) Export", "5) Cleanup & Reset" });
else
stepSelector.Items.AddRange(new string[] { "1) Import/Extract Frames", "2) Run Interpolation", "3) Export", "4) Cleanup & Reset" });
stepSelector.SelectedIndex = 0;
}
bool stepByStep = Config.GetInt("processingMode") == 1;
runBtn.Visible = !stepByStep && !Program.busy;
}
private async void runStepBtn_Click(object sender, EventArgs e)
{
SetTab("interpolate");
await InterpolateSteps.Run(stepSelector.Text);
}
private void mainTabControl_SelectedIndexChanged(object sender, EventArgs e)
{
if (!initialized) return;
aiCombox_SelectedIndexChanged(null, null);
}
}
}

2457
Code5/Form1.resx Normal file

File diff suppressed because it is too large Load Diff

226
Code5/Forms/BatchForm.Designer.cs generated Normal file
View File

@@ -0,0 +1,226 @@
namespace Flowframes.Forms
{
partial class BatchForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(BatchForm));
this.titleLabel = new System.Windows.Forms.Label();
this.stopBtn = new System.Windows.Forms.Button();
this.runBtn = new System.Windows.Forms.Button();
this.addToQueue = new System.Windows.Forms.Button();
this.forceStopBtn = new System.Windows.Forms.Button();
this.clearBtn = new System.Windows.Forms.Button();
this.taskList = new System.Windows.Forms.ListBox();
this.clearSelectedBtn = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.panel1 = new System.Windows.Forms.Panel();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// titleLabel
//
this.titleLabel.AutoSize = true;
this.titleLabel.Font = new System.Drawing.Font("Yu Gothic UI", 21.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.titleLabel.ForeColor = System.Drawing.Color.White;
this.titleLabel.Location = new System.Drawing.Point(12, 9);
this.titleLabel.Margin = new System.Windows.Forms.Padding(3, 0, 3, 10);
this.titleLabel.Name = "titleLabel";
this.titleLabel.Size = new System.Drawing.Size(323, 40);
this.titleLabel.TabIndex = 1;
this.titleLabel.Text = "Batch Processing Queue";
//
// stopBtn
//
this.stopBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.stopBtn.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.stopBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.stopBtn.ForeColor = System.Drawing.Color.White;
this.stopBtn.Location = new System.Drawing.Point(682, 351);
this.stopBtn.Name = "stopBtn";
this.stopBtn.Size = new System.Drawing.Size(250, 40);
this.stopBtn.TabIndex = 35;
this.stopBtn.Text = "Stop After Current Task";
this.stopBtn.UseVisualStyleBackColor = false;
this.stopBtn.Visible = false;
this.stopBtn.Click += new System.EventHandler(this.stopBtn_Click);
//
// runBtn
//
this.runBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.runBtn.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.runBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.runBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.runBtn.ForeColor = System.Drawing.Color.White;
this.runBtn.Location = new System.Drawing.Point(682, 443);
this.runBtn.Name = "runBtn";
this.runBtn.Size = new System.Drawing.Size(250, 40);
this.runBtn.TabIndex = 36;
this.runBtn.Text = "Start";
this.runBtn.UseVisualStyleBackColor = false;
this.runBtn.Click += new System.EventHandler(this.runBtn_Click);
//
// addToQueue
//
this.addToQueue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.addToQueue.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.addToQueue.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.addToQueue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.addToQueue.ForeColor = System.Drawing.Color.White;
this.addToQueue.Location = new System.Drawing.Point(682, 65);
this.addToQueue.Name = "addToQueue";
this.addToQueue.Size = new System.Drawing.Size(250, 40);
this.addToQueue.TabIndex = 39;
this.addToQueue.Text = "Add Current Configuration To Queue";
this.addToQueue.UseVisualStyleBackColor = false;
this.addToQueue.Click += new System.EventHandler(this.addToQueue_Click);
//
// forceStopBtn
//
this.forceStopBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.forceStopBtn.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.forceStopBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.forceStopBtn.ForeColor = System.Drawing.Color.White;
this.forceStopBtn.Location = new System.Drawing.Point(682, 397);
this.forceStopBtn.Name = "forceStopBtn";
this.forceStopBtn.Size = new System.Drawing.Size(250, 40);
this.forceStopBtn.TabIndex = 40;
this.forceStopBtn.Text = "Force Stop Now";
this.forceStopBtn.UseVisualStyleBackColor = false;
this.forceStopBtn.Visible = false;
this.forceStopBtn.Click += new System.EventHandler(this.forceStopBtn_Click);
//
// clearBtn
//
this.clearBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.clearBtn.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.clearBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.clearBtn.ForeColor = System.Drawing.Color.White;
this.clearBtn.Location = new System.Drawing.Point(682, 157);
this.clearBtn.Name = "clearBtn";
this.clearBtn.Size = new System.Drawing.Size(250, 40);
this.clearBtn.TabIndex = 41;
this.clearBtn.Text = "Clear All Queue Entries";
this.clearBtn.UseVisualStyleBackColor = false;
this.clearBtn.Click += new System.EventHandler(this.clearBtn_Click);
//
// taskList
//
this.taskList.AllowDrop = true;
this.taskList.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.taskList.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.taskList.ForeColor = System.Drawing.Color.White;
this.taskList.FormattingEnabled = true;
this.taskList.ItemHeight = 18;
this.taskList.Location = new System.Drawing.Point(12, 65);
this.taskList.Name = "taskList";
this.taskList.Size = new System.Drawing.Size(664, 418);
this.taskList.TabIndex = 43;
this.taskList.SelectedIndexChanged += new System.EventHandler(this.taskList_SelectedIndexChanged);
this.taskList.DragDrop += new System.Windows.Forms.DragEventHandler(this.taskList_DragDrop);
this.taskList.DragEnter += new System.Windows.Forms.DragEventHandler(this.taskList_DragEnter);
//
// clearSelectedBtn
//
this.clearSelectedBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.clearSelectedBtn.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.clearSelectedBtn.Enabled = false;
this.clearSelectedBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.clearSelectedBtn.ForeColor = System.Drawing.Color.White;
this.clearSelectedBtn.Location = new System.Drawing.Point(682, 111);
this.clearSelectedBtn.Name = "clearSelectedBtn";
this.clearSelectedBtn.Size = new System.Drawing.Size(250, 40);
this.clearSelectedBtn.TabIndex = 44;
this.clearSelectedBtn.Text = "Clear Selected Queue Entry";
this.clearSelectedBtn.UseVisualStyleBackColor = false;
this.clearSelectedBtn.Click += new System.EventHandler(this.clearSelectedBtn_Click);
//
// label1
//
this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(6, 6);
this.label1.Margin = new System.Windows.Forms.Padding(6);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(238, 111);
this.label1.TabIndex = 45;
this.label1.Text = "Tip:\r\nYou can also drag and drop multiple videos into the list.\r\nThey will be add" +
"ed to the queue using the interpolation settings set in the GUI.";
//
// panel1
//
this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(40)))), ((int)(((byte)(40)))), ((int)(((byte)(40)))));
this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.panel1.Controls.Add(this.label1);
this.panel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.panel1.Location = new System.Drawing.Point(682, 203);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(250, 142);
this.panel1.TabIndex = 46;
//
// BatchForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
this.ClientSize = new System.Drawing.Size(944, 501);
this.Controls.Add(this.panel1);
this.Controls.Add(this.clearSelectedBtn);
this.Controls.Add(this.taskList);
this.Controls.Add(this.clearBtn);
this.Controls.Add(this.forceStopBtn);
this.Controls.Add(this.addToQueue);
this.Controls.Add(this.runBtn);
this.Controls.Add(this.stopBtn);
this.Controls.Add(this.titleLabel);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.Name = "BatchForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Batch Processing";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.BatchForm_FormClosing);
this.Load += new System.EventHandler(this.BatchForm_Load);
this.panel1.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label titleLabel;
private System.Windows.Forms.Button stopBtn;
private System.Windows.Forms.Button runBtn;
private System.Windows.Forms.Button addToQueue;
private System.Windows.Forms.Button forceStopBtn;
private System.Windows.Forms.Button clearBtn;
private System.Windows.Forms.ListBox taskList;
private System.Windows.Forms.Button clearSelectedBtn;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Panel panel1;
}
}

150
Code5/Forms/BatchForm.cs Normal file
View File

@@ -0,0 +1,150 @@
using Flowframes.IO;
using Flowframes.Main;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.Forms
{
public partial class BatchForm : Form
{
public BatchForm()
{
AutoScaleMode = AutoScaleMode.None;
InitializeComponent();
BatchProcessing.currentBatchForm = this;
}
private void addToQueue_Click(object sender, EventArgs e)
{
Program.batchQueue.Enqueue(Program.mainForm.GetCurrentSettings());
RefreshGui();
}
public void RefreshGui ()
{
taskList.Items.Clear();
string nl = Environment.NewLine;
for (int i = 0; i < Program.batchQueue.Count; i++)
{
InterpSettings entry = Program.batchQueue.ElementAt(i);
string niceOutMode = entry.outMode.ToString().ToUpper().Replace("VID", "").Replace("IMG", "");
string str = $"#{i}: {Path.GetFileName(entry.inPath).Trunc(45)} - {entry.inFps} FPS => {entry.interpFactor}x{nl} {entry.ai.aiNameShort} => {niceOutMode}";
taskList.Items.Add(str);
}
}
public void SetWorking (bool working)
{
runBtn.Enabled = !working;
addToQueue.Enabled = !working;
stopBtn.Visible = working;
forceStopBtn.Visible = working;
stopBtn.Enabled = working;
}
private void BatchForm_Load(object sender, EventArgs e)
{
SetWorking(BatchProcessing.busy);
RefreshGui();
}
private void runBtn_Click(object sender, EventArgs e)
{
stopBtn.Enabled = true;
BatchProcessing.Start();
//WindowState = FormWindowState.Minimized;
Program.mainForm.WindowState = FormWindowState.Normal;
Program.mainForm.BringToFront();
}
private void clearBtn_Click(object sender, EventArgs e)
{
Program.batchQueue.Clear();
RefreshGui();
}
private void stopBtn_Click(object sender, EventArgs e)
{
stopBtn.Enabled = false;
BatchProcessing.stopped = true;
}
private void forceStopBtn_Click(object sender, EventArgs e)
{
Interpolate.Cancel("Force stopped by user.");
BatchProcessing.stopped = true;
}
private void BatchForm_FormClosing(object sender, FormClosingEventArgs e)
{
BatchProcessing.currentBatchForm = null;
}
private void clearSelectedBtn_Click(object sender, EventArgs e)
{
if (taskList.SelectedItem == null) return;
Queue<InterpSettings> temp = new Queue<InterpSettings>();
for(int i = 0; i < Program.batchQueue.Count; i++)
{
if (i != taskList.SelectedIndex)
temp.Enqueue(Program.batchQueue.ElementAt(i));
}
Program.batchQueue = temp;
RefreshGui();
}
private void taskList_SelectedIndexChanged(object sender, EventArgs e)
{
clearSelectedBtn.Enabled = taskList.SelectedItem != null;
}
private void taskList_DragEnter(object sender, DragEventArgs e) { e.Effect = DragDropEffects.Copy; }
private async void taskList_DragDrop(object sender, DragEventArgs e)
{
string[] droppedPaths = (string[])e.Data.GetData(DataFormats.FileDrop);
await LoadDroppedPaths(droppedPaths);
}
public async Task LoadDroppedPaths (string[] droppedPaths)
{
foreach (string path in droppedPaths)
{
Logger.Log($"Dropped file: '{path}'", true);
string frame1 = Path.Combine(path, "00000001.png");
if (IOUtils.IsPathDirectory(path) && !File.Exists(frame1))
{
InterpolateUtils.ShowMessage($"Can't find frames in this folder:\n\n{frame1} does not exist.", "Error");
continue;
}
InterpSettings current = Program.mainForm.GetCurrentSettings();
current.UpdatePaths(path, path.GetParentDir());
current.inFps = GetFramerate(path);
current.outFps = current.inFps * current.interpFactor;
Program.batchQueue.Enqueue(current);
RefreshGui();
await Task.Delay(100);
}
}
float GetFramerate (string path)
{
float fps = Interpolate.current.inFps;
float fpsFromFile = IOUtils.GetFpsFolderOrVideo(path);
if (fpsFromFile > 0)
return fpsFromFile;
return fps;
}
}
}

2445
Code5/Forms/BatchForm.resx Normal file

File diff suppressed because it is too large Load Diff

70
Code5/Forms/BigPreviewForm.Designer.cs generated Normal file
View File

@@ -0,0 +1,70 @@
namespace Flowframes.Forms
{
partial class BigPreviewForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(BigPreviewForm));
this.picBox = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)(this.picBox)).BeginInit();
this.SuspendLayout();
//
// picBox
//
this.picBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.picBox.Image = global::Flowframes.Properties.Resources.baseline_image_white_48dp_4x_25pcAlpha;
this.picBox.Location = new System.Drawing.Point(0, 0);
this.picBox.Margin = new System.Windows.Forms.Padding(0);
this.picBox.Name = "picBox";
this.picBox.Size = new System.Drawing.Size(704, 601);
this.picBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.picBox.TabIndex = 0;
this.picBox.TabStop = false;
//
// BigPreviewForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
this.ClientSize = new System.Drawing.Size(704, 601);
this.Controls.Add(this.picBox);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "BigPreviewForm";
this.Text = "Resizable Preview";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.BigPreviewForm_FormClosing);
this.Load += new System.EventHandler(this.BigPreviewForm_Load);
((System.ComponentModel.ISupportInitialize)(this.picBox)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.PictureBox picBox;
}
}

View File

@@ -0,0 +1,36 @@
using Flowframes.Main;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.Forms
{
public partial class BigPreviewForm : Form
{
public BigPreviewForm()
{
InitializeComponent();
}
private void BigPreviewForm_Load(object sender, EventArgs e)
{
}
public void SetImage (Image img)
{
picBox.Image = img;
}
private void BigPreviewForm_FormClosing(object sender, FormClosingEventArgs e)
{
InterpolateUtils.bigPreviewForm = null;
}
}
}

File diff suppressed because it is too large Load Diff

2033
Code5/Forms/SettingsForm.Designer.cs generated Normal file

File diff suppressed because it is too large Load Diff

197
Code5/Forms/SettingsForm.cs Normal file
View File

@@ -0,0 +1,197 @@
using Flowframes.IO;
using Flowframes.MiscUtils;
using Microsoft.WindowsAPICodePack.Dialogs;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.Forms
{
public partial class SettingsForm : Form
{
bool initialized = false;
public SettingsForm()
{
AutoScaleMode = AutoScaleMode.None;
InitializeComponent();
}
private void SettingsForm_Load(object sender, EventArgs e)
{
LoadSettings();
initialized = true;
CheckModelCacheSize();
}
public async Task CheckModelCacheSize ()
{
long modelFoldersBytes = 0;
foreach (string modelFolder in ModelDownloader.GetAllModelFolders())
modelFoldersBytes += IOUtils.GetDirSize(modelFolder, true);
if (modelFoldersBytes > 1024 * 1024)
clearModelCacheBtn.Text = $"Clear Model Cache ({FormatUtils.Bytes(modelFoldersBytes)})";
else
clearModelCacheBtn.Enabled = false;
}
private void SettingsForm_FormClosing(object sender, FormClosingEventArgs e)
{
SaveSettings();
Program.mainForm.UpdateStepByStepControls(true);
}
void SaveSettings ()
{
// Clamp...
h264Crf.Text = h264Crf.GetInt().Clamp(0, 51).ToString();
h265Crf.Text = h265Crf.GetInt().Clamp(0, 51).ToString();
vp9Crf.Text = vp9Crf.GetInt().Clamp(0, 63).ToString();
// Remove spaces...
torchGpus.Text = torchGpus.Text.Replace(" ", "");
ncnnGpus.Text = ncnnGpus.Text.Replace(" ", "");
// General
ConfigParser.SaveComboxIndex(processingMode);
ConfigParser.SaveGuiElement(maxVidHeight, ConfigParser.StringMode.Int);
ConfigParser.SaveGuiElement(enableAlpha);
ConfigParser.SaveComboxIndex(tempFolderLoc);
ConfigParser.SaveGuiElement(keepTempFolder);
ConfigParser.SaveGuiElement(delLogsOnStartup);
ConfigParser.SaveGuiElement(clearLogOnInput);
ConfigParser.SaveGuiElement(modelSuffix);
// Interpolation
ConfigParser.SaveGuiElement(keepAudio);
ConfigParser.SaveGuiElement(keepSubs);
ConfigParser.SaveComboxIndex(dedupMode);
ConfigParser.SaveComboxIndex(mpdecimateMode);
ConfigParser.SaveGuiElement(dedupThresh);
ConfigParser.SaveGuiElement(enableLoop);
ConfigParser.SaveGuiElement(scnDetect);
ConfigParser.SaveGuiElement(scnDetectValue);
ConfigParser.SaveComboxIndex(autoEncMode);
ConfigParser.SaveGuiElement(sbsAllowAutoEnc);
// AI
ConfigParser.SaveGuiElement(torchGpus);
ConfigParser.SaveGuiElement(ncnnGpus);
ConfigParser.SaveGuiElement(ncnnThreads);
ConfigParser.SaveGuiElement(uhdThresh);
ConfigParser.SaveGuiElement(dainNcnnTilesize, ConfigParser.StringMode.Int);
// Video Export
ConfigParser.SaveGuiElement(minOutVidLength, ConfigParser.StringMode.Int);
ConfigParser.SaveComboxIndex(mp4Enc);
ConfigParser.SaveGuiElement(h264Crf);
ConfigParser.SaveGuiElement(h265Crf);
ConfigParser.SaveGuiElement(vp9Crf);
ConfigParser.SaveComboxIndex(proResProfile);
ConfigParser.SaveGuiElement(gifColors);
ConfigParser.SaveGuiElement(aviCodec);
ConfigParser.SaveGuiElement(aviColors);
ConfigParser.SaveGuiElement(maxFps);
ConfigParser.SaveComboxIndex(maxFpsMode);
ConfigParser.SaveComboxIndex(loopMode);
// Debugging
ConfigParser.SaveComboxIndex(cmdDebugMode);
ConfigParser.SaveGuiElement(autoDedupFrames);
ConfigParser.SaveGuiElement(ffEncThreads, ConfigParser.StringMode.Int);
ConfigParser.SaveGuiElement(ffEncPreset);
ConfigParser.SaveGuiElement(ffEncArgs);
ConfigParser.SaveGuiElement(ffprobeCountFrames);
}
void LoadSettings()
{
// General
ConfigParser.LoadComboxIndex(processingMode);
ConfigParser.LoadGuiElement(maxVidHeight);
ConfigParser.LoadGuiElement(enableAlpha);
ConfigParser.LoadComboxIndex(tempFolderLoc); ConfigParser.LoadGuiElement(tempDirCustom);
ConfigParser.LoadGuiElement(delLogsOnStartup);
ConfigParser.LoadGuiElement(keepTempFolder);
ConfigParser.LoadGuiElement(clearLogOnInput);
ConfigParser.LoadGuiElement(modelSuffix);
// Interpolation
ConfigParser.LoadGuiElement(keepAudio);
ConfigParser.LoadGuiElement(keepSubs);
ConfigParser.LoadComboxIndex(dedupMode);
ConfigParser.LoadComboxIndex(mpdecimateMode);
ConfigParser.LoadGuiElement(dedupThresh);
ConfigParser.LoadGuiElement(enableLoop);
ConfigParser.LoadGuiElement(scnDetect);
ConfigParser.LoadGuiElement(scnDetectValue);
ConfigParser.LoadComboxIndex(autoEncMode);
ConfigParser.LoadGuiElement(sbsAllowAutoEnc);
// AI
ConfigParser.LoadGuiElement(torchGpus);
ConfigParser.LoadGuiElement(ncnnGpus);
ConfigParser.LoadGuiElement(ncnnThreads);
ConfigParser.LoadGuiElement(uhdThresh);
ConfigParser.LoadGuiElement(dainNcnnTilesize);
// Video Export
ConfigParser.LoadGuiElement(minOutVidLength);
ConfigParser.LoadComboxIndex(mp4Enc);
ConfigParser.LoadGuiElement(h264Crf);
ConfigParser.LoadGuiElement(h265Crf);
ConfigParser.LoadGuiElement(vp9Crf);
ConfigParser.LoadComboxIndex(proResProfile);
ConfigParser.LoadGuiElement(gifColors);
ConfigParser.LoadGuiElement(aviCodec);
ConfigParser.LoadGuiElement(aviColors);
ConfigParser.LoadGuiElement(maxFps);
ConfigParser.LoadComboxIndex(maxFpsMode);
ConfigParser.LoadComboxIndex(loopMode);
// Debugging
ConfigParser.LoadComboxIndex(cmdDebugMode);
ConfigParser.LoadGuiElement(autoDedupFrames);
ConfigParser.LoadGuiElement(ffEncThreads);
ConfigParser.LoadGuiElement(ffEncPreset);
ConfigParser.LoadGuiElement(ffEncArgs);
ConfigParser.LoadGuiElement(ffprobeCountFrames);
}
private void tempFolderLoc_SelectedIndexChanged(object sender, EventArgs e)
{
tempDirBrowseBtn.Visible = tempFolderLoc.SelectedIndex == 4;
tempDirCustom.Visible = tempFolderLoc.SelectedIndex == 4;
}
private void tempDirBrowseBtn_Click(object sender, EventArgs e)
{
CommonOpenFileDialog dialog = new CommonOpenFileDialog();
dialog.InitialDirectory = tempDirCustom.Text.Trim();
dialog.IsFolderPicker = true;
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
tempDirCustom.Text = dialog.FileName;
ConfigParser.SaveGuiElement(tempDirCustom);
}
private void cmdDebugMode_SelectedIndexChanged(object sender, EventArgs e)
{
if (initialized && cmdDebugMode.SelectedIndex == 2)
MessageBox.Show("If you enable this, you need to close the CMD window manually after the process has finished, otherwise processing will be paused!", "Notice");
}
private void dedupMode_SelectedIndexChanged(object sender, EventArgs e)
{
dedupeSensLabel.Visible = dedupMode.SelectedIndex != 0;
magickDedupePanel.Visible = dedupMode.SelectedIndex == 1;
mpDedupePanel.Visible = dedupMode.SelectedIndex == 2;
}
private void clearModelCacheBtn_Click(object sender, EventArgs e)
{
ModelDownloader.DeleteAllModels();
clearModelCacheBtn.Text = "Clear Model Cache";
CheckModelCacheSize();
}
}
}

File diff suppressed because it is too large Load Diff

208
Code5/Forms/UpdaterForm.Designer.cs generated Normal file
View File

@@ -0,0 +1,208 @@
namespace Flowframes.Forms
{
partial class UpdaterForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(UpdaterForm));
this.titleLabel = new System.Windows.Forms.Label();
this.label13 = new System.Windows.Forms.Label();
this.updatePatreonBtn = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.installedLabel = new System.Windows.Forms.Label();
this.latestLabel = new System.Windows.Forms.Label();
this.statusLabel = new System.Windows.Forms.Label();
this.downloadingLabel = new System.Windows.Forms.Label();
this.updateFreeBtn = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// titleLabel
//
this.titleLabel.AutoSize = true;
this.titleLabel.Font = new System.Drawing.Font("Yu Gothic UI", 21.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.titleLabel.ForeColor = System.Drawing.Color.White;
this.titleLabel.Location = new System.Drawing.Point(12, 9);
this.titleLabel.Margin = new System.Windows.Forms.Padding(3, 0, 3, 10);
this.titleLabel.Name = "titleLabel";
this.titleLabel.Size = new System.Drawing.Size(121, 40);
this.titleLabel.TabIndex = 2;
this.titleLabel.Text = "Updater";
//
// label13
//
this.label13.AutoSize = true;
this.label13.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label13.ForeColor = System.Drawing.Color.White;
this.label13.Location = new System.Drawing.Point(17, 67);
this.label13.Margin = new System.Windows.Forms.Padding(8, 8, 3, 0);
this.label13.Name = "label13";
this.label13.Size = new System.Drawing.Size(110, 16);
this.label13.TabIndex = 35;
this.label13.Text = "Installed Version:";
//
// updatePatreonBtn
//
this.updatePatreonBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.updatePatreonBtn.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.updatePatreonBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.updatePatreonBtn.ForeColor = System.Drawing.Color.White;
this.updatePatreonBtn.Location = new System.Drawing.Point(12, 229);
this.updatePatreonBtn.Name = "updatePatreonBtn";
this.updatePatreonBtn.Size = new System.Drawing.Size(203, 40);
this.updatePatreonBtn.TabIndex = 36;
this.updatePatreonBtn.Text = "Download Patreon Version";
this.updatePatreonBtn.UseVisualStyleBackColor = true;
this.updatePatreonBtn.Click += new System.EventHandler(this.updatePatreonBtn_Click);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label1.ForeColor = System.Drawing.Color.White;
this.label1.Location = new System.Drawing.Point(16, 93);
this.label1.Margin = new System.Windows.Forms.Padding(8, 10, 3, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(96, 16);
this.label1.TabIndex = 37;
this.label1.Text = "Latest Version:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label2.ForeColor = System.Drawing.Color.White;
this.label2.Location = new System.Drawing.Point(16, 119);
this.label2.Margin = new System.Windows.Forms.Padding(8, 10, 3, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(48, 16);
this.label2.TabIndex = 38;
this.label2.Text = "Status:";
//
// installedLabel
//
this.installedLabel.AutoSize = true;
this.installedLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.installedLabel.ForeColor = System.Drawing.Color.White;
this.installedLabel.Location = new System.Drawing.Point(200, 67);
this.installedLabel.Margin = new System.Windows.Forms.Padding(8, 8, 3, 0);
this.installedLabel.Name = "installedLabel";
this.installedLabel.Size = new System.Drawing.Size(76, 16);
this.installedLabel.TabIndex = 39;
this.installedLabel.Text = "Loading...";
//
// latestLabel
//
this.latestLabel.AutoSize = true;
this.latestLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.latestLabel.ForeColor = System.Drawing.Color.White;
this.latestLabel.Location = new System.Drawing.Point(200, 93);
this.latestLabel.Margin = new System.Windows.Forms.Padding(8, 8, 3, 0);
this.latestLabel.Name = "latestLabel";
this.latestLabel.Size = new System.Drawing.Size(76, 16);
this.latestLabel.TabIndex = 40;
this.latestLabel.Text = "Loading...";
//
// statusLabel
//
this.statusLabel.AutoSize = true;
this.statusLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.statusLabel.ForeColor = System.Drawing.Color.White;
this.statusLabel.Location = new System.Drawing.Point(200, 119);
this.statusLabel.Margin = new System.Windows.Forms.Padding(8, 8, 3, 0);
this.statusLabel.Name = "statusLabel";
this.statusLabel.Size = new System.Drawing.Size(76, 16);
this.statusLabel.TabIndex = 41;
this.statusLabel.Text = "Loading...";
//
// downloadingLabel
//
this.downloadingLabel.AutoSize = true;
this.downloadingLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.downloadingLabel.ForeColor = System.Drawing.Color.White;
this.downloadingLabel.Location = new System.Drawing.Point(226, 241);
this.downloadingLabel.Margin = new System.Windows.Forms.Padding(8, 10, 3, 0);
this.downloadingLabel.Name = "downloadingLabel";
this.downloadingLabel.Size = new System.Drawing.Size(0, 16);
this.downloadingLabel.TabIndex = 42;
//
// updateFreeBtn
//
this.updateFreeBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.updateFreeBtn.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(48)))), ((int)(((byte)(48)))), ((int)(((byte)(48)))));
this.updateFreeBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.updateFreeBtn.ForeColor = System.Drawing.Color.White;
this.updateFreeBtn.Location = new System.Drawing.Point(221, 229);
this.updateFreeBtn.Name = "updateFreeBtn";
this.updateFreeBtn.Size = new System.Drawing.Size(203, 40);
this.updateFreeBtn.TabIndex = 43;
this.updateFreeBtn.Text = "Download Free Version";
this.updateFreeBtn.UseVisualStyleBackColor = true;
this.updateFreeBtn.Click += new System.EventHandler(this.updateFreeBtn_Click);
//
// UpdaterForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(32)))), ((int)(((byte)(32)))), ((int)(((byte)(32)))));
this.ClientSize = new System.Drawing.Size(624, 281);
this.Controls.Add(this.updateFreeBtn);
this.Controls.Add(this.downloadingLabel);
this.Controls.Add(this.statusLabel);
this.Controls.Add(this.latestLabel);
this.Controls.Add(this.installedLabel);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.updatePatreonBtn);
this.Controls.Add(this.label13);
this.Controls.Add(this.titleLabel);
this.ForeColor = System.Drawing.Color.White;
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "UpdaterForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Updater";
this.Load += new System.EventHandler(this.UpdaterForm_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label titleLabel;
private System.Windows.Forms.Label label13;
private System.Windows.Forms.Button updatePatreonBtn;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label installedLabel;
private System.Windows.Forms.Label latestLabel;
private System.Windows.Forms.Label statusLabel;
private System.Windows.Forms.Label downloadingLabel;
private System.Windows.Forms.Button updateFreeBtn;
}
}

View File

@@ -0,0 +1,82 @@
using Flowframes.Data;
using Flowframes.OS;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.Forms
{
public partial class UpdaterForm : Form
{
SemVer installed;
SemVer latestPat;
SemVer latestFree;
public UpdaterForm()
{
AutoScaleMode = AutoScaleMode.None;
InitializeComponent();
}
private async void UpdaterForm_Load(object sender, EventArgs e)
{
installed = Updater.GetInstalledVer();
latestPat = Updater.GetLatestVer(true);
latestFree = Updater.GetLatestVer(false);
installedLabel.Text = installed.ToString();
await Task.Delay(100);
latestLabel.Text = $"{latestPat} (Patreon/Beta) - {latestFree} (Free/Stable)";
if (Updater.VersionMatches(installed, latestFree))
{
statusLabel.Text = "Latest Free Version Is Installed.";
if (Updater.IsVersionNewer(installed, latestPat))
statusLabel.Text += "\nBeta Update Available On Patreon.";
return;
}
if (Updater.VersionMatches(installed, latestPat))
{
statusLabel.Text = "Latest Patreon/Beta Version Is Installed.";
return;
}
if (Updater.IsVersionNewer(installed, latestPat))
{
statusLabel.Text = "Update available on Patreon!";
if (Updater.IsVersionNewer(installed, latestFree))
statusLabel.Text += " - Beta Updates Available On Patreon and Itch.io.";
return;
}
}
float lastProg = -1f;
public void SetProgLabel (float prog, string str)
{
if (prog == lastProg) return;
lastProg = prog;
downloadingLabel.Text = str;
}
private void updatePatreonBtn_Click(object sender, EventArgs e)
{
string link = Updater.GetLatestVerLink(true);
if(!string.IsNullOrWhiteSpace(link))
Process.Start(link);
}
private void updateFreeBtn_Click(object sender, EventArgs e)
{
string link = Updater.GetLatestVerLink(false);
if (!string.IsNullOrWhiteSpace(link))
Process.Start(link);
}
}
}

2445
Code5/Forms/UpdaterForm.resx Normal file

File diff suppressed because it is too large Load Diff

15
Code5/IO/CfgStrings.cs Normal file
View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.IO
{
class CfgStrings
{
// public static string dedupMode = "dedupMode";
// public static string dedupThresh = "dedupThresh";
// public static string keepFrames = "keepFrames";
}
}

179
Code5/IO/Config.cs Normal file
View File

@@ -0,0 +1,179 @@
using Flowframes.OS;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace Flowframes.IO
{
internal class Config
{
private static string configPath;
private static string[] cachedLines;
public static void Init()
{
configPath = Path.Combine(Paths.GetDataPath(), "config.ini");
IOUtils.CreateFileIfNotExists(configPath);
Reload();
}
public static void Set(string key, string value)
{
string[] lines = new string[1];
try
{
lines = File.ReadAllLines(configPath);
}
catch
{
MessageBox.Show("Failed to read config file!\nFlowframes will try to re-create the file if it does not exist.", "Error");
if(!File.Exists(configPath))
Init();
}
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Split('|')[0] == key)
{
lines[i] = key + "|" + value;
File.WriteAllLines(configPath, lines);
cachedLines = lines;
return;
}
}
List<string> list = lines.ToList();
list.Add(key + "|" + value);
list = list.OrderBy(p => p).ToList();
string newFileContent = "";
foreach(string line in list)
newFileContent += line + "\n";
File.WriteAllText(configPath, newFileContent.Trim());
cachedLines = list.ToArray();
}
public static string Get(string key, Type type = Type.String)
{
try
{
for (int i = 0; i < cachedLines.Length; i++)
{
string[] keyValuePair = cachedLines[i].Split('|');
if (keyValuePair[0] == key && !string.IsNullOrWhiteSpace(keyValuePair[1]))
return keyValuePair[1];
}
return WriteDefaultValIfExists(key, type);
}
catch (Exception e)
{
Logger.Log($"Failed to get {key.Wrap()} from config! {e.Message}");
}
return null;
}
public static bool GetBool(string key)
{
return bool.Parse(Get(key, Type.Bool));
}
public static bool GetBool(string key, bool defaultVal)
{
WriteIfDoesntExist(key, (defaultVal ? "True" : "False"));
return bool.Parse(Get(key, Type.Bool));
}
public static int GetInt(string key)
{
return Get(key, Type.Int).GetInt();
}
public static int GetInt(string key, int defaultVal)
{
WriteIfDoesntExist(key, defaultVal.ToString());
return GetInt(key);
}
public static float GetFloat(string key)
{
return float.Parse(Get(key, Type.Float), CultureInfo.InvariantCulture);
}
public static string GetFloatString (string key)
{
return Get(key, Type.Float).Replace(",", ".");
}
static void WriteIfDoesntExist (string key, string val)
{
foreach (string line in cachedLines)
if (line.Contains(key + "|"))
return;
Set(key, val);
}
public enum Type { String, Int, Float, Bool }
private static string WriteDefaultValIfExists(string key, Type type)
{
if (key == "maxVidHeight") return WriteDefault(key, "2160");
if (key == "delLogsOnStartup") return WriteDefault(key, "True");
if (key == "clearLogOnInput") return WriteDefault(key, "True");
if (key == "tempDirCustom") return WriteDefault(key, "C:/");
// Interpolation
if (key == "dedupMode") return WriteDefault(key, "2");
if (key == "dedupThresh") return WriteDefault(key, "2");
if (key == "keepAudio") return WriteDefault(key, "True");
if (key == "keepSubs") return WriteDefault(key, "True");
if (key == "autoDedupFrames") return WriteDefault(key, "100");
if (key == "vfrDedupe") return WriteDefault(key, "True");
if (key == "scnDetectValue") return WriteDefault(key, "0.2");
if (key == "autoEncMode") return WriteDefault(key, "2");
// Video Export
if (key == "h264Crf") return WriteDefault(key, "20");
if (key == "h265Crf") return WriteDefault(key, "24");
if (key == "vp9Crf") return WriteDefault(key, "32");
if (key == "proResProfile") return WriteDefault(key, "2");
if (key == "aviCodec") return WriteDefault(key, "ffv1");
if (key == "aviColors") return WriteDefault(key, "yuv420p");
if (key == "gifColors") return WriteDefault(key, "128 (High)");
if (key == "minVidLength") return WriteDefault(key, "2");
// AI
if (key == "uhdThresh") return WriteDefault(key, "1440");
if (key == "ncnnThreads") return WriteDefault(key, "1");
if (key == "dainNcnnTilesize") return WriteDefault(key, "768");
// Debug / Other / Experimental
if (key == "ffEncPreset") return WriteDefault(key, "medium");
if (key == "ffEncArgs") return WriteDefault(key, "");
// Tile Sizes
if (key == "tilesize_RIFE_NCNN") return WriteDefault(key, "2048");
if (key == "tilesize_DAIN_NCNN") return WriteDefault(key, "512");
if (key == "tilesize_CAIN_NCNN") return WriteDefault(key, "2048");
if (type == Type.Int || type == Type.Float) return WriteDefault(key, "0"); // Write default int/float (0)
if (type == Type.Bool) return WriteDefault(key, "False"); // Write default bool (False)
return WriteDefault(key, "0");
}
private static string WriteDefault(string key, string def)
{
Set(key, def);
return def;
}
private static void Reload()
{
List<string> validLines = new List<string>();
string[] lines = File.ReadAllLines(configPath);
foreach (string line in lines)
{
if(line != null && !string.IsNullOrWhiteSpace(line) && line.Length > 3)
validLines.Add(line);
}
cachedLines = validLines.ToArray();
}
}
}

80
Code5/IO/ConfigParser.cs Normal file
View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.IO
{
class ConfigParser
{
public enum StringMode { Any, Int, Float }
public static void SaveGuiElement(TextBox textbox, StringMode stringMode = StringMode.Any)
{
switch (stringMode)
{
case StringMode.Any: Config.Set(textbox.Name, textbox.Text); break;
case StringMode.Int: Config.Set(textbox.Name, textbox.Text.GetInt().ToString()); break;
case StringMode.Float: Config.Set(textbox.Name, textbox.Text.GetFloat().ToString()); break;
}
}
public static void SaveGuiElement(ComboBox comboBox, StringMode stringMode = StringMode.Any)
{
switch (stringMode)
{
case StringMode.Any: Config.Set(comboBox.Name, comboBox.Text); break;
case StringMode.Int: Config.Set(comboBox.Name, comboBox.Text.GetInt().ToString()); break;
case StringMode.Float: Config.Set(comboBox.Name, comboBox.Text.GetFloat().ToString().Replace(",", ".")); break;
}
}
public static void SaveGuiElement(CheckBox checkbox)
{
Config.Set(checkbox.Name, checkbox.Checked.ToString());
}
public static void SaveGuiElement(NumericUpDown upDown, StringMode stringMode = StringMode.Any)
{
switch (stringMode)
{
case StringMode.Any: Config.Set(upDown.Name, ((float)upDown.Value).ToString().Replace(",", ".")); break;
case StringMode.Int: Config.Set(upDown.Name, ((int)upDown.Value).ToString()); break;
case StringMode.Float: Config.Set(upDown.Name, ((float)upDown.Value).ToString().Replace(",", ".")); ; break;
}
}
public static void SaveComboxIndex(ComboBox comboBox)
{
Config.Set(comboBox.Name, comboBox.SelectedIndex.ToString());
}
public static void LoadGuiElement(ComboBox comboBox, string suffix = "")
{
comboBox.Text = Config.Get(comboBox.Name) + suffix;
}
public static void LoadGuiElement(TextBox textbox, string suffix = "")
{
textbox.Text = Config.Get(textbox.Name) + suffix; ;
}
public static void LoadGuiElement(CheckBox checkbox)
{
checkbox.Checked = Config.GetBool(checkbox.Name);
}
public static void LoadGuiElement(NumericUpDown upDown)
{
upDown.Value = Convert.ToDecimal(Config.GetFloat(upDown.Name));
}
public static void LoadComboxIndex(ComboBox comboBox)
{
comboBox.SelectedIndex = Config.GetInt(comboBox.Name);
}
}
}

745
Code5/IO/IOUtils.cs Normal file
View File

@@ -0,0 +1,745 @@

using Flowframes.Data;
using Flowframes.Main;
using Flowframes.MiscUtils;
using Flowframes.UI;
using Force.Crc32;
using Microsoft.WindowsAPICodePack.Shell;
using Standart.Hash.xxHash;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.IO
{
class IOUtils
{
public static string GetExe()
{
return System.Reflection.Assembly.GetEntryAssembly().GetName().CodeBase.Replace("file:///", "");
}
public static string GetExeDir()
{
return AppDomain.CurrentDomain.BaseDirectory;
}
public static Image GetImage(string path)
{
using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
return Image.FromStream(stream);
}
public static string[] ReadLines(string path)
{
List<string> lines = new List<string>();
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 0x1000, FileOptions.SequentialScan))
using (var sr = new StreamReader(fs, Encoding.UTF8))
{
string line;
while ((line = sr.ReadLine()) != null)
lines.Add(line);
}
return lines.ToArray();
}
public static bool IsPathDirectory(string path)
{
if (path == null)
{
throw new ArgumentNullException("path");
}
path = path.Trim();
if (Directory.Exists(path))
{
return true;
}
if (File.Exists(path))
{
return false;
}
if (new string[2]
{
"\\",
"/"
}.Any((string x) => path.EndsWith(x)))
{
return true;
}
return string.IsNullOrWhiteSpace(Path.GetExtension(path));
}
public static bool IsFileValid(string path)
{
if (path == null)
return false;
if (!File.Exists(path))
return false;
return true;
}
public static bool IsDirValid(string path)
{
if (path == null)
return false;
if (!Directory.Exists(path))
return false;
return true;
}
public static void Copy(string sourceDirectoryName, string targetDirectoryName, bool move = false)
{
Directory.CreateDirectory(targetDirectoryName);
DirectoryInfo source = new DirectoryInfo(sourceDirectoryName);
DirectoryInfo target = new DirectoryInfo(targetDirectoryName);
CopyWork(source, target, move);
}
private static void CopyWork(DirectoryInfo source, DirectoryInfo target, bool move)
{
DirectoryInfo[] directories = source.GetDirectories();
foreach (DirectoryInfo directoryInfo in directories)
{
CopyWork(directoryInfo, target.CreateSubdirectory(directoryInfo.Name), move);
}
FileInfo[] files = source.GetFiles();
foreach (FileInfo fileInfo in files)
{
if (move)
fileInfo.MoveTo(Path.Combine(target.FullName, fileInfo.Name));
else
fileInfo.CopyTo(Path.Combine(target.FullName, fileInfo.Name), overwrite: true);
}
}
public static void DeleteContentsOfDir(string path)
{
DirectoryInfo directoryInfo = new DirectoryInfo(path);
FileInfo[] files = directoryInfo.GetFiles();
foreach (FileInfo fileInfo in files)
{
fileInfo.Delete();
}
DirectoryInfo[] directories = directoryInfo.GetDirectories();
foreach (DirectoryInfo directoryInfo2 in directories)
{
directoryInfo2.Delete(recursive: true);
}
}
public static void ReplaceInFilenamesDir(string dir, string textToFind, string textToReplace, bool recursive = true, string wildcard = "*")
{
int counter = 1;
DirectoryInfo d = new DirectoryInfo(dir);
FileInfo[] files = null;
if (recursive)
files = d.GetFiles(wildcard, SearchOption.AllDirectories);
else
files = d.GetFiles(wildcard, SearchOption.TopDirectoryOnly);
foreach (FileInfo file in files)
{
ReplaceInFilename(file.FullName, textToFind, textToReplace);
counter++;
}
}
public static void ReplaceInFilename(string path, string textToFind, string textToReplace)
{
string ext = Path.GetExtension(path);
string newFilename = Path.GetFileNameWithoutExtension(path).Replace(textToFind, textToReplace);
string targetPath = Path.Combine(Path.GetDirectoryName(path), newFilename + ext);
if (File.Exists(targetPath))
{
//Program.Print("Skipped " + path + " because a file with the target name already exists.");
return;
}
File.Move(path, targetPath);
}
public static bool TryCopy(string source, string dest, bool overwrite = true) // Copy with error handling. Returns false if failed
{
try
{
File.Copy(source, dest, overwrite);
}
catch (Exception e)
{
MessageBox.Show("Copy from \"" + source + "\" to \"" + dest + " (Overwrite: " + overwrite + ") failed: \n\n" + e.Message);
return false;
}
return true;
}
public static int GetFilenameCounterLength(string file, string prefixToRemove = "")
{
string filenameNoExt = Path.GetFileNameWithoutExtension(file);
if (!string.IsNullOrEmpty(prefixToRemove))
filenameNoExt = filenameNoExt.Replace(prefixToRemove, "");
string onlyNumbersFilename = Regex.Replace(filenameNoExt, "[^.0-9]", "");
return onlyNumbersFilename.Length;
}
public static int GetAmountOfFiles (string path, bool recursive, string wildcard = "*")
{
try
{
DirectoryInfo d = new DirectoryInfo(path);
FileInfo[] files = null;
if (recursive)
files = d.GetFiles(wildcard, SearchOption.AllDirectories);
else
files = d.GetFiles(wildcard, SearchOption.TopDirectoryOnly);
return files.Length;
}
catch
{
return 0;
}
}
static bool TryCopy(string source, string target)
{
try
{
File.Copy(source, target);
}
catch
{
return false;
}
return true;
}
static bool TryMove(string source, string target, bool deleteIfExists = true)
{
try
{
if (deleteIfExists && File.Exists(target))
File.Delete(target);
File.Move(source, target);
}
catch
{
return false;
}
return true;
}
public static void RenameCounterDir(string path, bool inverse = false)
{
int counter = 1;
DirectoryInfo d = new DirectoryInfo(path);
FileInfo[] files = d.GetFiles();
var filesSorted = files.OrderBy(n => n);
if (inverse)
filesSorted.Reverse();
foreach (FileInfo file in files)
{
string dir = new DirectoryInfo(file.FullName).Parent.FullName;
int filesDigits = (int)Math.Floor(Math.Log10((double)files.Length) + 1);
File.Move(file.FullName, Path.Combine(dir, counter.ToString().PadLeft(filesDigits, '0') + Path.GetExtension(file.FullName)));
counter++;
//if (counter % 100 == 0) Program.Print("Renamed " + counter + " files...");
}
}
public static Dictionary<string, string> RenameCounterDirReversible(string path, string ext, int startAt, int padding = 0)
{
Dictionary<string, string> oldNewNamesMap = new Dictionary<string, string>();
int counter = startAt;
FileInfo[] files = new DirectoryInfo(path).GetFiles($"*.{ext}", SearchOption.TopDirectoryOnly);
var filesSorted = files.OrderBy(n => n);
foreach (FileInfo file in files)
{
string dir = new DirectoryInfo(file.FullName).Parent.FullName;
int filesDigits = (int)Math.Floor(Math.Log10((double)files.Length) + 1);
string outpath = "";
if (padding > 0)
outpath = Path.Combine(dir, counter.ToString().PadLeft(padding, '0') + Path.GetExtension(file.FullName));
else
outpath = Path.Combine(dir, counter.ToString() + Path.GetExtension(file.FullName));
File.Move(file.FullName, outpath);
oldNewNamesMap.Add(file.FullName, outpath);
counter++;
}
return oldNewNamesMap;
}
public static void ReverseRenaming(Dictionary<string, string> oldNewMap, bool clearDict)
{
if (oldNewMap == null || oldNewMap.Count < 1) return;
foreach (KeyValuePair<string, string> pair in oldNewMap)
TryMove(pair.Value, pair.Key);
if (clearDict)
oldNewMap.Clear();
}
public static float GetVideoFramerate (string path)
{
float fps = 0;
try
{
ShellFile shellFile = ShellFile.FromFilePath(path);
fps = (float)shellFile.Properties.System.Video.FrameRate.Value / 1000f;
Logger.Log("Detected FPS of " + Path.GetFileName(path) + " as " + fps + " FPS", true);
if (fps <= 0)
throw new Exception("FPS is 0.");
}
catch
{
Logger.Log("Failed to read FPS - Trying alternative method...", true);
try
{
fps = FFmpegCommands.GetFramerate(path);
Logger.Log("Detected FPS of " + Path.GetFileName(path) + " as " + fps + " FPS", true);
}
catch
{
Logger.Log("Failed to read FPS - Please enter it manually.");
}
}
return fps;
}
public static float GetVideoFramerateForDir(string path)
{
float fps = 0;
try
{
string parentDir = path.GetParentDir();
string fpsFile = Path.Combine(parentDir, "fps.ini");
fps = float.Parse(ReadLines(fpsFile)[0]);
Logger.Log($"Got {fps} FPS from file: " + fpsFile);
float guiFps = Program.mainForm.GetCurrentSettings().inFps;
DialogResult dialogResult = MessageBox.Show("A frame rate file has been found in the parent directory.\n\n" +
$"Click \"Yes\" to use frame rate from the file ({fps}) or \"No\" to use current FPS set in GUI ({guiFps})", "Load Frame Rate From fps.ini?", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
return fps;
else if (dialogResult == DialogResult.No)
return guiFps;
}
catch { }
return fps;
}
public static async Task<Size> GetVideoOrFramesRes (string path)
{
Size res = new Size();
if (!IsPathDirectory(path)) // If path is video
{
res = GetVideoRes(path);
}
else // Path is frame folder
{
Image thumb = await MainUiFunctions.GetThumbnail(path);
res = new Size(thumb.Width, thumb.Height);
}
return res;
}
public static Size GetVideoRes (string path)
{
Size size = new Size(0, 0);
try
{
ShellFile shellFile = ShellFile.FromFilePath(path);
int w = (int)shellFile.Properties.System.Video.FrameWidth.Value;
int h = (int)shellFile.Properties.System.Video.FrameHeight.Value;
return new Size(w, h);
}
catch (Exception e)
{
Logger.Log($"Failed to read video size ({e.Message}) - Trying alternative method...", true);
try
{
size = FFmpegCommands.GetSize(path);
Logger.Log($"Detected video size of {Path.GetFileName(path)} as {size.Width}x{size.Height}", true);
}
catch
{
Logger.Log("Failed to read video size!");
}
}
return size;
}
public static bool TryDeleteIfExists(string path) // Returns true if no exception occurs
{
try
{
if (path == null)
return false;
DeleteIfExists(path);
return true;
}
catch (Exception e)
{
Logger.Log($"TryDeleteIfExists: Error trying to delete {path}: {e.Message}", true);
return false;
}
}
public static bool DeleteIfExists (string path) // Returns true if the file/dir exists
{
if (!IsPathDirectory(path) && File.Exists(path))
{
File.Delete(path);
return true;
}
if (IsPathDirectory(path) && Directory.Exists(path))
{
Directory.Delete(path, true);
return true;
}
return false;
}
public static string GetCurrentExportSuffix ()
{
return GetExportSuffix(Interpolate.current.interpFactor, Interpolate.current.ai, Interpolate.current.model);
}
public static string GetExportSuffix(int factor, AI ai, string mdl)
{
string suffix = $"-{factor}x-{ai.aiNameShort.ToUpper()}";
if (Config.GetBool("modelSuffix"))
suffix += $"-{mdl}";
return suffix;
}
public static string GetHighestFrameNumPath (string path)
{
FileInfo highest = null;
int highestInt = -1;
foreach(FileInfo frame in new DirectoryInfo(path).GetFiles("*.*", SearchOption.TopDirectoryOnly))
{
int num = frame.Name.GetInt();
if (num > highestInt)
{
highest = frame;
highestInt = frame.Name.GetInt();
}
}
return highest.FullName;
}
public static string FilenameSuffix (string path, string suffix)
{
try
{
string ext = Path.GetExtension(path);
return Path.Combine(path.GetParentDir(), $"{Path.GetFileNameWithoutExtension(path)}{suffix}{ext}");
}
catch
{
return path;
}
}
public static string GetAudioFile (string basePath)
{
string[] exts = new string[] { "m4a", "wav", "ogg", "mp2", "mp3" };
foreach(string ext in exts)
{
string filename = Path.ChangeExtension(basePath, ext);
if (File.Exists(filename))
return filename;
}
return null;
}
public static float GetFpsFolderOrVideo(string path)
{
try
{
if (IsPathDirectory(path))
{
float dirFps = GetVideoFramerateForDir(path);
if (dirFps > 0)
return dirFps;
}
else
{
float vidFps = GetVideoFramerate(path);
if (vidFps > 0)
return vidFps;
}
}
catch (Exception e)
{
Logger.Log("GetFpsFolderOrVideo() Error: " + e.Message);
}
return 0;
}
public enum ErrorMode { HiddenLog, VisibleLog, Messagebox }
public static bool CanWriteToDir (string dir, ErrorMode errMode)
{
string tempFile = Path.Combine(dir, "flowframes-testfile.tmp");
try
{
File.Create(tempFile);
File.Delete(tempFile);
return true;
}
catch (Exception e)
{
Logger.Log($"Can't write to {dir}! {e.Message}", errMode == ErrorMode.HiddenLog);
if (errMode == ErrorMode.Messagebox && !BatchProcessing.busy)
MessageBox.Show($"Can't write to {dir}!\n\n{e.Message}", "Error");
return false;
}
}
public static bool CopyTo (string file, string targetFolder, bool overwrite = true)
{
string targetPath = Path.Combine(targetFolder, Path.GetFileName(file));
try
{
if (!Directory.Exists(targetFolder))
Directory.CreateDirectory(targetFolder);
File.Copy(file, targetPath, overwrite);
return true;
}
catch (Exception e)
{
Logger.Log($"Failed to copy {file} to {targetFolder}: {e.Message}");
return false;
}
}
public static bool MoveTo(string file, string targetFolder, bool overwrite = true)
{
string targetPath = Path.Combine(targetFolder, Path.GetFileName(file));
try
{
if (!Directory.Exists(targetFolder))
Directory.CreateDirectory(targetFolder);
if (overwrite)
DeleteIfExists(targetPath);
File.Move(file, targetPath);
return true;
}
catch (Exception e)
{
Logger.Log($"Failed to move {file} to {targetFolder}: {e.Message}");
return false;
}
}
public enum Hash { MD5, CRC32, xxHash }
public static string GetHash (string path, Hash hashType, bool log = true, bool quick = true)
{
Benchmarker.Start();
string hashStr = "";
if (IsPathDirectory(path))
{
Logger.Log($"Path '{path}' is directory! Returning empty hash.", true);
return hashStr;
}
try
{
var stream = File.OpenRead(path);
if (hashType == Hash.MD5)
{
MD5 md5 = MD5.Create();
var hash = md5.ComputeHash(stream);
hashStr = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
if (hashType == Hash.CRC32)
{
var crc = new Crc32Algorithm();
var crc32bytes = crc.ComputeHash(stream);
hashStr = BitConverter.ToUInt32(crc32bytes, 0).ToString();
}
if (hashType == Hash.xxHash)
{
ulong xxh64 = xxHash64.ComputeHash(stream, 8192, (ulong)GetFilesize(path));
hashStr = xxh64.ToString();
}
stream.Close();
}
catch (Exception e)
{
Logger.Log($"Error getting file hash for {Path.GetFileName(path)}: {e.Message}", true);
return "";
}
if (log)
Logger.Log($"Computed {hashType} for '{Path.GetFileNameWithoutExtension(path).Trunc(40) + Path.GetExtension(path)}' ({GetFilesizeStr(path)}) in {Benchmarker.GetTimeStr(true)}: {hashStr}", true);
return hashStr;
}
public static async Task<string> GetHashAsync(string path, Hash hashType, bool log = true, bool quick = true)
{
await Task.Delay(1);
return GetHash(path, hashType, log, quick);
}
public static bool CreateDir (string path) // Returns whether the dir already existed
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
return false;
}
return true;
}
public static void ZeroPadDir(string path, string ext, int targetLength, bool recursive = false)
{
FileInfo[] files;
if (recursive)
files = new DirectoryInfo(path).GetFiles($"*.{ext}", SearchOption.AllDirectories);
else
files = new DirectoryInfo(path).GetFiles($"*.{ext}", SearchOption.TopDirectoryOnly);
ZeroPadDir(files.Select(x => x.FullName).ToList(), targetLength);
}
public static void ZeroPadDir(List<string> files, int targetLength, List<string> exclude = null, bool noLog = true)
{
if(exclude != null)
files = files.Except(exclude).ToList();
foreach (string file in files)
{
string fname = Path.GetFileNameWithoutExtension(file);
string targetFilename = Path.Combine(Path.GetDirectoryName(file), fname.PadLeft(targetLength, '0') + Path.GetExtension(file));
try
{
if (targetFilename != file)
File.Move(file, targetFilename);
}
catch (Exception e)
{
if(!noLog)
Logger.Log($"Failed to zero-pad {file} => {targetFilename}: {e.Message}", true);
}
}
}
public static bool CheckImageValid (string path)
{
try
{
Image img = GetImage(path);
if (img.Width > 1 && img.Height > 1)
return true;
return false;
}
catch
{
return false;
}
}
public static string[] GetFilesSorted (string path, bool recursive = false, string pattern = "*")
{
SearchOption opt = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
return Directory.GetFiles(path, pattern, opt).OrderBy(x => Path.GetFileName(x)).ToArray();
}
public static string[] GetFilesSorted(string path, string pattern = "*")
{
return GetFilesSorted(path, false, pattern);
}
public static string[] GetFilesSorted(string path)
{
return GetFilesSorted(path, false, "*");
}
public static FileInfo[] GetFileInfosSorted(string path, bool recursive = false, string pattern = "*")
{
SearchOption opt = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
DirectoryInfo dir = new DirectoryInfo(path);
return dir.GetFiles(pattern, opt).OrderBy(x => x.Name).ToArray();
}
public static bool CreateFileIfNotExists (string path)
{
if (File.Exists(path))
return false;
try
{
File.Create(path).Close();
return true;
}
catch (Exception e)
{
Logger.Log($"Failed to create file at '{path}': {e.Message}");
return false;
}
}
public static long GetDirSize(string path, bool recursive, string[] includedExtensions = null)
{
long size = 0;
// Add file sizes.
string[] files;
StringComparison ignCase = StringComparison.OrdinalIgnoreCase;
if (includedExtensions == null)
files = Directory.GetFiles(path);
else
files = Directory.GetFiles(path).Where(file => includedExtensions.Any(x => file.EndsWith(x, ignCase))).ToArray();
foreach (string file in files)
size += new FileInfo(file).Length;
if (!recursive)
return size;
// Add subdirectory sizes.
DirectoryInfo[] dis = new DirectoryInfo(path).GetDirectories();
foreach (DirectoryInfo di in dis)
size += GetDirSize(di.FullName, true, includedExtensions);
return size;
}
public static long GetFilesize(string path)
{
return new FileInfo(path).Length;
}
public static string GetFilesizeStr (string path)
{
return FormatUtils.Bytes(GetFilesize(path));
}
public static byte[] GetLastBytes (string path, int startAt, int bytesAmount)
{
byte[] buffer = new byte[bytesAmount];
using (BinaryReader reader = new BinaryReader(new FileStream(path, FileMode.Open)))
{
reader.BaseStream.Seek(startAt, SeekOrigin.Begin);
reader.Read(buffer, 0, bytesAmount);
}
return buffer;
}
}
}

110
Code5/IO/Logger.cs Normal file
View File

@@ -0,0 +1,110 @@
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DT = System.DateTime;
namespace Flowframes
{
class Logger
{
public static TextBox textbox;
static string file;
public const string defaultLogName = "sessionlog";
public static long id;
public static void Log(string s, bool hidden = false, bool replaceLastLine = false, string filename = "")
{
if (string.IsNullOrWhiteSpace(s))
return;
Console.WriteLine(s);
try
{
if (replaceLastLine)
textbox.Text = textbox.Text.Remove(textbox.Text.LastIndexOf(Environment.NewLine));
}
catch { }
s = s.Replace("\n", Environment.NewLine);
if (!hidden && textbox != null)
textbox.AppendText((textbox.Text.Length > 1 ? Environment.NewLine : "") + s);
LogToFile(s, false, filename);
}
public static void LogToFile(string logStr, bool noLineBreak, string filename)
{
if (string.IsNullOrWhiteSpace(filename))
filename = defaultLogName;
if (Path.GetExtension(filename) != ".txt")
filename = Path.ChangeExtension(filename, "txt");
file = Path.Combine(Paths.GetLogPath(), filename);
logStr = logStr.Replace(Environment.NewLine, " ").TrimWhitespaces();
string time = DT.Now.Month + "-" + DT.Now.Day + "-" + DT.Now.Year + " " + DT.Now.Hour + ":" + DT.Now.Minute + ":" + DT.Now.Second;
try
{
if (!noLineBreak)
File.AppendAllText(file, $"{Environment.NewLine}[{id}] [{time}]: {logStr}");
else
File.AppendAllText(file, " " + logStr);
id++;
}
catch
{
// this if fine, i forgot why
}
}
public static void WriteToFile (string content, bool append, string filename)
{
if (string.IsNullOrWhiteSpace(filename))
filename = defaultLogName;
if (Path.GetExtension(filename) != ".txt")
filename = Path.ChangeExtension(filename, "txt");
file = Path.Combine(Paths.GetLogPath(), filename);
string time = DT.Now.Month + "-" + DT.Now.Day + "-" + DT.Now.Year + " " + DT.Now.Hour + ":" + DT.Now.Minute + ":" + DT.Now.Second;
try
{
if (append)
File.AppendAllText(file, Environment.NewLine + time + ":" + Environment.NewLine + content);
else
File.WriteAllText(file, Environment.NewLine + time + ":" + Environment.NewLine + content);
}
catch
{
}
}
public static void ClearLogBox ()
{
textbox.Text = "";
}
public static string GetLastLine ()
{
string[] lines = textbox.Text.SplitIntoLines();
if (lines.Length < 1)
return "";
return lines.Last();
}
public static void RemoveLastLine ()
{
textbox.Text = textbox.Text.Remove(textbox.Text.LastIndexOf(Environment.NewLine));
}
}
}

202
Code5/IO/ModelDownloader.cs Normal file
View File

@@ -0,0 +1,202 @@
using Flowframes.Data;
using Flowframes.MiscUtils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.IO
{
class ModelDownloader
{
static string baseUrl = "https://dl.nmkd.de/flowframes/mdl/";
public static async Task<Dictionary<string, string>> GetFilelist (string ai, string model)
{
var client = new WebClient();
string[] fileLines = client.DownloadString(GetMdlFileUrl(ai, model, "md5.txt")).SplitIntoLines();
Dictionary<string, string> filesDict = GetDict(fileLines);
return filesDict;
}
static string GetMdlUrl (string ai, string model)
{
return Path.Combine(baseUrl, ai.ToLower(), model);
}
static string GetMdlFileUrl(string ai, string model, string file)
{
return Path.Combine(GetMdlUrl(ai, model), file);
}
static string GetLocalPath(string ai, string model)
{
return Path.Combine(Paths.GetPkgPath(), ai, model);
}
static async Task DownloadTo (string url, string saveDir, int retries = 3)
{
string savePath = Path.Combine(saveDir, Path.GetFileName(url));
IOUtils.TryDeleteIfExists(savePath);
Logger.Log($"Downloading '{url}' to '{savePath}'", true);
Stopwatch sw = new Stopwatch();
sw.Restart();
bool completed = false;
int lastProgPercentage = -1;
var client = new WebClient();
client.DownloadProgressChanged += (sender, args) =>
{
if (sw.ElapsedMilliseconds > 200 && args.ProgressPercentage != lastProgPercentage)
{
sw.Restart();
lastProgPercentage = args.ProgressPercentage;
Logger.Log($"Downloading model file '{Path.GetFileName(url)}'... {args.ProgressPercentage}%", false, true);
}
};
client.DownloadFileCompleted += (sender, args) =>
{
if (args.Error != null)
Logger.Log("Download failed: " + args.Error.Message);
completed = true;
};
client.DownloadFileTaskAsync(url, savePath).ConfigureAwait(false);
while (!completed)
{
if (Interpolate.canceled)
{
client.CancelAsync();
client.Dispose();
return;
}
if (sw.ElapsedMilliseconds > 6000)
{
client.CancelAsync();
if(retries > 0)
{
await DownloadTo(url, saveDir, retries--);
}
else
{
Interpolate.Cancel("Model download failed.");
return;
}
}
await Task.Delay(500);
}
Logger.Log($"Downloaded '{Path.GetFileName(url)}' ({IOUtils.GetFilesize(savePath) / 1024} KB)", true);
}
public static async Task DownloadModelFiles (string ai, string model)
{
model = model.ToUpper();
Logger.Log($"DownloadModelFiles(string ai = {ai}, string model = {model})", true);
try
{
string mdlDir = GetLocalPath(ai, model);
if (AreFilesValid(ai, model))
return;
Logger.Log($"Downloading '{model}' model files...");
Directory.CreateDirectory(mdlDir);
await DownloadTo(GetMdlFileUrl(ai, model, "md5.txt"), mdlDir);
Dictionary<string, string> fileList = await GetFilelist(ai, model);
foreach (KeyValuePair<string, string> modelFile in fileList)
await DownloadTo(GetMdlFileUrl(ai, model, modelFile.Key), mdlDir);
Logger.Log($"Downloaded \"{model}\" model files.", false, true);
}
catch (Exception e)
{
Logger.Log($"DownloadModelFiles Error: {e.Message}\nStack Trace:\n{e.StackTrace}");
Interpolate.Cancel($"Error downloading model files: {e.Message}");
}
}
public static void DeleteAllModels ()
{
foreach(string modelFolder in GetAllModelFolders())
{
string size = FormatUtils.Bytes(IOUtils.GetDirSize(modelFolder, true));
if (IOUtils.TryDeleteIfExists(modelFolder))
Logger.Log($"Deleted cached model '{Path.GetFileName(modelFolder.GetParentDir())}/{Path.GetFileName(modelFolder)}' ({size})");
}
}
public static List<string> GetAllModelFolders()
{
List<string> modelPaths = new List<string>();
foreach (AI ai in Networks.networks)
{
string aiPkgFolder = PkgUtils.GetPkgFolder(ai.pkg);
string modelsFile = Path.Combine(aiPkgFolder, "models.txt");
if (!File.Exists(modelsFile)) continue;
foreach (string mdl in IOUtils.ReadLines(modelsFile))
{
string modelName = mdl.Split('-')[0].Remove(" ").Remove(".");
string mdlFolder = Path.Combine(aiPkgFolder, modelName);
if (!Directory.Exists(mdlFolder)) continue;
modelPaths.Add(mdlFolder);
}
}
return modelPaths;
}
public static bool AreFilesValid (string ai, string model)
{
string mdlDir = GetLocalPath(ai, model);
if (!Directory.Exists(mdlDir))
{
Logger.Log($"Files for model {model} not valid: {mdlDir} does not exist.", true);
return false;
}
string md5FilePath = Path.Combine(mdlDir, "md5.txt");
if (!File.Exists(md5FilePath) || IOUtils.GetFilesize(md5FilePath) < 32)
{
Logger.Log($"Files for model {model} not valid: {mdlDir} does not exist or is incomplete.", true);
return false;
}
string[] md5Lines = IOUtils.ReadLines(md5FilePath);
Dictionary<string, string> filesDict = GetDict(md5Lines);
foreach(KeyValuePair<string, string> file in filesDict)
{
string md5 = IOUtils.GetHash(Path.Combine(mdlDir, file.Key), IOUtils.Hash.MD5);
if (md5.Trim() != file.Value.Trim())
{
Logger.Log($"Files for model {model} not valid: MD5 of {file.Key} ({md5.Trim()}) does not equal validation MD5 ({file.Value.Trim()}).", true);
return false;
}
}
return true;
}
static Dictionary<string, string> GetDict (string[] lines, char sep = ':')
{
Dictionary<string, string> dict = new Dictionary<string, string>();
foreach (string line in lines)
{
if (line.Length < 3) continue;
string[] keyValuePair = line.Split(':');
dict.Add(keyValuePair[0], keyValuePair[1]);
}
return dict;
}
}
}

49
Code5/IO/Paths.cs Normal file
View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.IO
{
class Paths
{
public const string framesDir = "frames";
public const string interpDir = "interp";
public const string chunksDir = "vchunks";
public const string scenesDir = "scenes";
public static void Init()
{
}
public static string GetVerPath()
{
return Path.Combine(GetDataPath(), "ver.ini");
}
public static string GetDataPath ()
{
string path = Path.Combine(IOUtils.GetExeDir(), "FlowframesData");
Directory.CreateDirectory(path);
return path;
}
public static string GetPkgPath()
{
string path = Path.Combine(GetDataPath(), "pkgs");
Directory.CreateDirectory(path);
return path;
}
public static string GetLogPath()
{
string path = Path.Combine(GetDataPath(), "logs");
Directory.CreateDirectory(path);
return path;
}
}
}

43
Code5/IO/PkgUtils.cs Normal file
View File

@@ -0,0 +1,43 @@
using Flowframes.Data;
using Flowframes.Forms;
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
namespace Flowframes.IO
{
class PkgUtils
{
public static string GetPkgFolder (FlowPackage pkg)
{
return Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(pkg.fileName));
}
public static bool IsInstalled(FlowPackage pkg)
{
string path = GetPkgFolder(pkg);
return (Directory.Exists(path) && IOUtils.GetAmountOfFiles(path, true) > 0);
}
public static bool IsUpToDate(FlowPackage pkg, int minVersion)
{
return (GetVersion(pkg) >= minVersion);
}
public static int GetVersion (FlowPackage pkg)
{
string versionFilePath = Path.Combine(GetPkgFolder(pkg), "ver.ini");
if (!File.Exists(versionFilePath))
return 0;
return IOUtils.ReadLines(versionFilePath)[0].Split('#')[0].GetInt();
}
public static bool IsAiAvailable(AI ai, bool msg = true)
{
Logger.Log("PkgInstaller.IsAiAvailable - Checking for AI " + ai.aiName, true);
return IsInstalled(ai.pkg);
}
}
}

99
Code5/Magick/Converter.cs Normal file
View File

@@ -0,0 +1,99 @@
using Flowframes;
using Flowframes.IO;
using Flowframes.UI;
using ImageMagick;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Magick
{
class Converter
{
public static async Task Convert (string dir, MagickFormat format, int quality, string ext = "", bool print = true, bool setProgress = true)
{
var files = IOUtils.GetFilesSorted(dir);
if(print) Logger.Log($"Converting {files.Length} files in {dir}");
int counter = 0;
foreach (string file in files)
{
if (print) Logger.Log("Converting " + Path.GetFileName(file) + " to " + format.ToString().StripNumbers().ToUpper(), false, true);
MagickImage img = new MagickImage(file);
img.Format = format;
img.Quality = quality;
string outpath = file;
if (!string.IsNullOrWhiteSpace(ext)) outpath = Path.ChangeExtension(outpath, ext);
img.Write(outpath);
counter++;
if(setProgress)
Program.mainForm.SetProgress((int)Math.Round(((float)counter / files.Length) * 100f));
await Task.Delay(1);
}
}
public static async Task ExtractAlpha (string inputDir, string outputDir, bool print = true, bool setProgress = true)
{
try
{
var files = IOUtils.GetFilesSorted(inputDir);
if (print) Logger.Log($"Extracting alpha channel from images...");
Directory.CreateDirectory(outputDir);
Stopwatch sw = new Stopwatch();
sw.Restart();
int counter = 0;
foreach (string file in files)
{
MagickImage img = new MagickImage(file);
img.Format = MagickFormat.Png32;
img.Quality = 10;
img.FloodFill(MagickColors.None, 0, 0); // Fill the image with a transparent background
img.InverseOpaque(MagickColors.None, MagickColors.White); // Change all the pixels that are not transparent to white.
img.ColorAlpha(MagickColors.Black); // Change the transparent pixels to black.
string outPath = Path.Combine(outputDir, Path.GetFileName(file));
img.Write(outPath);
counter++;
if (sw.ElapsedMilliseconds > 250)
{
if (setProgress)
Program.mainForm.SetProgress((int)Math.Round(((float)counter / files.Length) * 100f));
await Task.Delay(1);
sw.Restart();
}
}
}
catch (Exception e)
{
Logger.Log("ExtractAlpha Error: " + e.Message);
}
}
public static async Task Preprocess (string dir, bool setProgress = true)
{
var files = IOUtils.GetFilesSorted(dir);
Logger.Log($"Preprocessing {files} files in {dir}");
int counter = 0;
foreach (string file in files)
{
//Logger.Log("Converting " + Path.GetFileName(file) + " to " + format, false, true);
MagickImage img = new MagickImage(file);
//img.Format = MagickFormat.Bmp;
//img.Write(file);
//img = new MagickImage(file);
img.Format = MagickFormat.Png24;
img.Quality = 10;
counter++;
if (setProgress)
Program.mainForm.SetProgress((int)Math.Round(((float)counter / files.Length) * 100f));
await Task.Delay(1);
}
}
}
}

234
Code5/Magick/Dedupe.cs Normal file
View File

@@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Flowframes.IO;
using ImageMagick;
using Flowframes.OS;
using Flowframes.Data;
using System.Drawing;
using Paths = Flowframes.IO.Paths;
namespace Flowframes.Magick
{
class Dedupe
{
public enum Mode { None, Info, Enabled, Auto }
public static Mode currentMode;
public static float currentThreshold;
public static async Task Run(string path, bool testRun = false, bool setStatus = true)
{
if (path == null || !Directory.Exists(path) || Interpolate.canceled)
return;
currentMode = Mode.Auto;
if(setStatus)
Program.mainForm.SetStatus("Running frame de-duplication");
currentThreshold = Config.GetFloat("dedupThresh");
Logger.Log("Running accurate frame de-duplication...");
if (currentMode == Mode.Enabled || currentMode == Mode.Auto)
await RemoveDupeFrames(path, currentThreshold, "png", testRun, false, (currentMode == Mode.Auto));
}
public static Dictionary<string, MagickImage> imageCache = new Dictionary<string, MagickImage>();
static MagickImage GetImage(string path)
{
bool allowCaching = true;
if (!allowCaching)
return new MagickImage(path);
if (!imageCache.ContainsKey(path))
imageCache.Add(path, new MagickImage(path));
return imageCache[path];
}
public static void ClearCache ()
{
imageCache.Clear();
}
public static async Task RemoveDupeFrames(string path, float threshold, string ext, bool testRun = false, bool debugLog = false, bool skipIfNoDupes = false)
{
Stopwatch sw = new Stopwatch();
sw.Restart();
Logger.Log("Removing duplicate frames - Threshold: " + threshold.ToString("0.00"));
FileInfo[] framePaths = IOUtils.GetFileInfosSorted(path, false, "*." + ext);
List<string> framesToDelete = new List<string>();
int bufferSize = await GetBufferSize();
int currentOutFrame = 1;
int currentDupeCount = 0;
int statsFramesKept = 0;
int statsFramesDeleted = 0;
int skipAfterNoDupesFrames = Config.GetInt("autoDedupFrames");
bool hasEncounteredAnyDupes = false;
bool skipped = false;
bool hasReachedEnd = false;
string fileContent = "";
for (int i = 0; i < framePaths.Length; i++) // Loop through frames
{
if (hasReachedEnd)
break;
string frame1 = framePaths[i].FullName;
int compareWithIndex = i + 1;
while (true) // Loop dupes
{
//compareWithIndex++;
if (compareWithIndex >= framePaths.Length)
{
hasReachedEnd = true;
break;
}
if (framesToDelete.Contains(framePaths[compareWithIndex].FullName) || !File.Exists(framePaths[compareWithIndex].FullName))
{
//Logger.Log($"Frame {compareWithIndex} was already deleted - skipping");
compareWithIndex++;
}
else
{
string frame2 = framePaths[compareWithIndex].FullName;
float diff = GetDifference(frame1, frame2);
if (diff < threshold) // Is a duped frame.
{
if (!testRun)
{
framesToDelete.Add(frame2);
if (debugLog) Logger.Log("[Deduplication] Deleted " + Path.GetFileName(frame2));
hasEncounteredAnyDupes = true;
}
statsFramesDeleted++;
currentDupeCount++;
}
else
{
fileContent += $"{Path.GetFileNameWithoutExtension(framePaths[i].Name)}:{currentDupeCount}\n";
statsFramesKept++;
currentOutFrame++;
currentDupeCount = 0;
break;
}
if (sw.ElapsedMilliseconds >= 500 || (i+1) == framePaths.Length) // Print every 0.5s (or when done)
{
sw.Restart();
Logger.Log($"[Deduplication] Running de-duplication ({i}/{framePaths.Length}), deleted {statsFramesDeleted} ({(((float)statsFramesDeleted / framePaths.Length) * 100f).ToString("0")}%) duplicate frames so far...", false, true);
Program.mainForm.SetProgress((int)Math.Round(((float)i / framePaths.Length) * 100f));
if (imageCache.Count > bufferSize || (imageCache.Count > 50 && OSUtils.GetFreeRamMb() < 3500))
ClearCache();
}
}
}
// int oldIndex = -1; // TODO: Compare with 1st to fix loops?
// if (i >= framePaths.Length) // If this is the last frame, compare with 1st to avoid OutOfRange error
// {
// oldIndex = i;
// i = 0;
// }
if(i % 3 == 0)
await Task.Delay(1);
if (Interpolate.canceled) return;
if (!testRun && skipIfNoDupes && !hasEncounteredAnyDupes && skipAfterNoDupesFrames > 0 && i >= skipAfterNoDupesFrames)
{
skipped = true;
break;
}
}
foreach (string frame in framesToDelete)
IOUtils.TryDeleteIfExists(frame);
string testStr = testRun ? " [TestRun]" : "";
if (Interpolate.canceled) return;
int framesLeft = IOUtils.GetAmountOfFiles(path, false, $"*.png");
int framesDeleted = framePaths.Length - framesLeft;
float percentDeleted = ((float)framesDeleted / framePaths.Length) * 100f;
string keptPercent = $"{(100f - percentDeleted).ToString("0.0")}%";
if (skipped)
Logger.Log($"[Deduplication] First {skipAfterNoDupesFrames} frames did not have any duplicates - Skipping the rest!", false, true);
else
Logger.Log($"[Deduplication]{testStr} Done. Kept {framesLeft} ({keptPercent}) frames, deleted {framesDeleted} frames.", false, true);
if (statsFramesKept <= 0)
Interpolate.Cancel("No frames were left after de-duplication!\n\nTry decreasing the de-duplication threshold.");
}
static float GetDifference (string img1Path, string img2Path)
{
MagickImage img2 = GetImage(img2Path);
MagickImage img1 = GetImage(img1Path);
double err = img1.Compare(img2, ErrorMetric.Fuzz);
float errPercent = (float)err * 100f;
return errPercent;
}
static async Task<int> GetBufferSize ()
{
Size res = await Interpolate.current.GetScaledRes();
long pixels = res.Width * res.Height; // 4K = 8294400, 1440p = 3686400, 1080p = 2073600, 720p = 921600, 540p = 518400, 360p = 230400
int bufferSize = 100;
if (pixels < 518400) bufferSize = 1800;
if (pixels >= 518400) bufferSize = 1400;
if (pixels >= 921600) bufferSize = 800;
if (pixels >= 2073600) bufferSize = 400;
if (pixels >= 3686400) bufferSize = 200;
if (pixels >= 8294400) bufferSize = 100;
if (pixels == 0) bufferSize = 100;
Logger.Log($"Using magick dedupe buffer size {bufferSize} for frame resolution {res.Width}x{res.Height}", true);
return bufferSize;
}
public static async Task CreateDupesFile (string framesPath, int lastFrameNum)
{
string infoFile = Path.Combine(framesPath.GetParentDir(), $"dupes.ini");
string fileContent = "";
FileInfo[] frameFiles = IOUtils.GetFileInfosSorted(framesPath, false, "*.png");
for(int i = 0; i < frameFiles.Length; i++)
{
bool isLastItem = (i + 1) == frameFiles.Length;
int frameNum1 = frameFiles[i].Name.GetInt();
int frameNum2 = isLastItem ? lastFrameNum : frameFiles[i+1].Name.GetInt();
int diff = frameNum2 - frameNum1;
int dupes = diff - 1;
//if (File.Exists(Path.Combine(framesPath.GetParentDir(), Paths.scenesDir, frameFiles[i].Name)))
// dupes = 0;
fileContent += $"{Path.GetFileNameWithoutExtension(frameFiles[i].Name)}:{dupes}\n";
}
File.WriteAllText(infoFile, fileContent);
}
}
}

178
Code5/Main/AutoEncode.cs Normal file
View File

@@ -0,0 +1,178 @@
using Flowframes.AudioVideo;
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.MiscUtils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Main
{
class AutoEncode
{
static string interpFramesFolder;
static string videoChunksFolder;
public static int chunkSize = 125; // Encode every n frames
public static int safetyBufferFrames = 90; // Ignore latest n frames to avoid using images that haven't been fully encoded yet
public static string[] interpFramesLines;
public static List<int> encodedFrameLines = new List<int>();
public static List<int> unencodedFrameLines = new List<int>();
public static bool busy;
public static bool paused;
public static void UpdateChunkAndBufferSizes ()
{
chunkSize = GetChunkSize(IOUtils.GetAmountOfFiles(Interpolate.current.framesFolder, false, "*.png") * Interpolate.current.interpFactor);
bool isNcnn = Interpolate.current.ai.aiName.ToUpper().Contains("NCNN");
safetyBufferFrames = isNcnn ? Config.GetInt("autoEncSafeBufferNcnn", 90) : Config.GetInt("autoEncSafeBufferCuda", 30); // Use bigger safety buffer for NCNN
}
public static async Task MainLoop(string interpFramesPath)
{
try
{
UpdateChunkAndBufferSizes();
interpFramesFolder = interpFramesPath;
videoChunksFolder = Path.Combine(interpFramesPath.GetParentDir(), Paths.chunksDir);
if (Interpolate.currentlyUsingAutoEnc)
Directory.CreateDirectory(videoChunksFolder);
encodedFrameLines.Clear();
unencodedFrameLines.Clear();
Logger.Log($"[AutoEnc] Starting AutoEncode MainLoop - Chunk Size: {chunkSize} Frames - Safety Buffer: {safetyBufferFrames} Frames", true);
int videoIndex = 1;
string encFile = Path.Combine(interpFramesPath.GetParentDir(), $"vfr-{Interpolate.current.interpFactor}x.ini");
interpFramesLines = IOUtils.ReadLines(encFile).Select(x => x.Split('/').Last().Remove("'").Split('#').First()).ToArray(); // Array with frame filenames
while (!Interpolate.canceled && GetInterpFramesAmount() < 2)
await Task.Delay(2000);
int lastEncodedFrameNum = 0;
while (HasWorkToDo()) // Loop while proc is running and not all frames have been encoded
{
if (Interpolate.canceled) return;
if (paused)
{
//Logger.Log("autoenc paused");
await Task.Delay(200);
continue;
}
unencodedFrameLines.Clear();
for (int vfrLine = lastEncodedFrameNum; vfrLine < interpFramesLines.Length; vfrLine++)
unencodedFrameLines.Add(vfrLine);
bool aiRunning = !AiProcess.currentAiProcess.HasExited;
if (unencodedFrameLines.Count > 0 && (unencodedFrameLines.Count >= (chunkSize + safetyBufferFrames) || !aiRunning)) // Encode every n frames, or after process has exited
{
List<int> frameLinesToEncode = aiRunning ? unencodedFrameLines.Take(chunkSize).ToList() : unencodedFrameLines; // Take all remaining frames if process is done
string lastOfChunk = Path.Combine(interpFramesPath, interpFramesLines[frameLinesToEncode.Last()]);
if (!File.Exists(lastOfChunk))
{
await Task.Delay(500);
continue;
}
busy = true;
string outpath = Path.Combine(videoChunksFolder, "chunks", $"{videoIndex.ToString().PadLeft(4, '0')}{FFmpegUtils.GetExt(Interpolate.current.outMode)}");
//int firstFrameNum = frameLinesToEncode[0];
int firstLineNum = frameLinesToEncode.First();
int lastLineNum = frameLinesToEncode.Last();
Logger.Log($"[AutoEnc] Encoding Chunk #{videoIndex} to '{outpath}' using line {firstLineNum} ({Path.GetFileName(interpFramesLines[firstLineNum])}) through {lastLineNum} ({Path.GetFileName(Path.GetFileName(interpFramesLines[frameLinesToEncode.Last()]))})", true, false, "ffmpeg");
await CreateVideo.EncodeChunk(outpath, Interpolate.current.outMode, firstLineNum, frameLinesToEncode.Count);
if (Interpolate.canceled) return;
if (Config.GetInt("autoEncMode") == 2)
Task.Run(() => DeleteOldFramesAsync(interpFramesPath, frameLinesToEncode));
if (Interpolate.canceled) return;
encodedFrameLines.AddRange(frameLinesToEncode);
Logger.Log("Done Encoding Chunk #" + videoIndex, true, false, "ffmpeg");
lastEncodedFrameNum = (frameLinesToEncode.Last() + 1 );
videoIndex++;
busy = false;
}
await Task.Delay(50);
}
if (Interpolate.canceled) return;
IOUtils.ReverseRenaming(AiProcess.filenameMap, true); // Get timestamps back
await CreateVideo.ChunksToVideos(Interpolate.current.tempFolder, videoChunksFolder, Interpolate.current.outFilename);
}
catch (Exception e)
{
Logger.Log($"AutoEnc Error: {e.Message}. Stack Trace:\n{e.StackTrace}");
Interpolate.Cancel("Auto-Encode encountered an error.");
}
}
static async Task DeleteOldFramesAsync (string interpFramesPath, List<int> frameLinesToEncode)
{
Logger.Log("[AutoEnc] Starting DeleteOldFramesAsync.", true, false, "ffmpeg");
Stopwatch sw = new Stopwatch();
sw.Restart();
foreach (int frame in frameLinesToEncode)
{
bool delete = !FrameIsStillNeeded(interpFramesLines[frame], frame);
if (delete) // Make sure frames are no longer needed (e.g. for dupes) before deleting!
{
string framePath = Path.Combine(interpFramesPath, interpFramesLines[frame]);
File.WriteAllText(framePath, "THIS IS A DUMMY FILE - DO NOT DELETE ME"); // Overwrite to save space without breaking progress counter
await Task.Delay(1);
}
}
Logger.Log("[AutoEnc] DeleteOldFramesAsync finished in " + FormatUtils.TimeSw(sw), true, false, "ffmpeg");
}
static bool FrameIsStillNeeded (string frameName, int frameIndex)
{
if ((frameIndex + 1) < interpFramesLines.Length && interpFramesLines[frameIndex+1].Contains(frameName))
return true;
return false;
}
public static bool HasWorkToDo ()
{
if (Interpolate.canceled || interpFramesFolder == null) return false;
// Logger.Log($"HasWorkToDo - Process Running: {(AiProcess.currentAiProcess != null && !AiProcess.currentAiProcess.HasExited)} - encodedFrameLines.Count: {encodedFrameLines.Count} - interpFramesLines.Length: {interpFramesLines.Length}");
return ((AiProcess.currentAiProcess != null && !AiProcess.currentAiProcess.HasExited) || encodedFrameLines.Count < interpFramesLines.Length);
}
static int GetChunkSize(int targetFramesAmount)
{
if (targetFramesAmount > 50000) return 2400;
if (targetFramesAmount > 20000) return 1200;
if (targetFramesAmount > 5000) return 600;
if (targetFramesAmount > 1000) return 300;
return 150;
}
static int GetInterpFramesAmount()
{
return IOUtils.GetAmountOfFiles(interpFramesFolder, false, $"*.{InterpolateUtils.GetOutExt()}");
}
}
}

View File

@@ -0,0 +1,110 @@
using Flowframes.Forms;
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Main
{
class BatchProcessing
{
public static bool stopped = false;
public static BatchForm currentBatchForm;
public static bool busy = false;
public static async void Start()
{
if (Config.GetBool("clearLogOnInput"))
Logger.ClearLogBox();
stopped = false;
Program.mainForm.SetTab("preview");
int initTaskCount = Program.batchQueue.Count;
for (int i = 0; i < initTaskCount; i++)
{
if (!stopped && Program.batchQueue.Count > 0)
{
Logger.Log($"[Queue] Running queue task {i + 1}/{initTaskCount}, {Program.batchQueue.Count} tasks left.");
await RunEntry(Program.batchQueue.Peek());
if (currentBatchForm != null)
currentBatchForm.RefreshGui();
}
await Task.Delay(1000);
}
Logger.Log("[Queue] Finished queue processing.");
SetBusy(false);
Program.mainForm.SetTab("interpolation");
}
public static void Stop()
{
stopped = true;
}
static async Task RunEntry(InterpSettings entry)
{
if (!EntryIsValid(entry))
{
Logger.Log("[Queue] Skipping entry because it's invalid.");
Program.batchQueue.Dequeue();
return;
}
string fname = Path.GetFileName(entry.inPath);
if (IOUtils.IsPathDirectory(entry.inPath)) fname = Path.GetDirectoryName(entry.inPath);
Logger.Log($"[Queue] Processing {fname} ({entry.interpFactor}x {entry.ai.aiNameShort}).");
SetBusy(true);
Program.mainForm.LoadBatchEntry(entry); // Load entry into GUI
Interpolate.current = entry;
Program.mainForm.runBtn_Click(null, null);
await Task.Delay(2000);
while (Program.busy)
await Task.Delay(1000);
SetBusy(false);
Program.batchQueue.Dequeue();
Logger.Log($"[Queue] Done processing {fname} ({entry.interpFactor}x {entry.ai.aiNameShort}).");
}
static void SetBusy(bool state)
{
busy = state;
if (currentBatchForm != null)
currentBatchForm.SetWorking(state);
Program.mainForm.SetWorking(state);
Program.mainForm.GetMainTabControl().Enabled = !state; // Lock GUI
}
static bool EntryIsValid(InterpSettings entry)
{
if (entry.inPath == null || (IOUtils.IsPathDirectory(entry.inPath) && !Directory.Exists(entry.inPath)) || (!IOUtils.IsPathDirectory(entry.inPath) && !File.Exists(entry.inPath)))
{
Logger.Log("[Queue] Can't process queue entry: Input path is invalid.");
return false;
}
if (entry.outPath == null || !Directory.Exists(entry.outPath))
{
Logger.Log("[Queue] Can't process queue entry: Output path is invalid.");
return false;
}
if (!PkgUtils.IsAiAvailable(entry.ai))
{
Logger.Log("[Queue] Can't process queue entry: Selected AI is not available.");
return false;
}
return true;
}
}
}

233
Code5/Main/CreateVideo.cs Normal file
View File

@@ -0,0 +1,233 @@
using Flowframes;
using Flowframes.IO;
using Flowframes.Magick;
using Flowframes.Main;
using Flowframes.OS;
using Flowframes.UI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Padding = Flowframes.Data.Padding;
using i = Flowframes.Interpolate;
using System.Diagnostics;
using Flowframes.AudioVideo;
namespace Flowframes.Main
{
class CreateVideo
{
static string currentOutFile; // Keeps track of the out file, in case it gets renamed (FPS limiting, looping, etc) before finishing export
public static async Task Export(string path, string outPath, i.OutMode mode)
{
if (!mode.ToString().ToLower().Contains("vid")) // Copy interp frames out of temp folder and skip video export for image seq export
{
try
{
await CopyOutputFrames(path, Path.GetFileNameWithoutExtension(outPath));
}
catch(Exception e)
{
Logger.Log("Failed to move interp frames folder: " + e.Message);
}
return;
}
if (IOUtils.GetAmountOfFiles(path, false, $"*.{InterpolateUtils.GetOutExt()}") <= 1)
{
i.Cancel("Output folder does not contain frames - An error must have occured during interpolation!", AiProcess.hasShownError);
return;
}
await Task.Delay(10);
Program.mainForm.SetStatus("Creating output video from frames...");
try
{
float maxFps = Config.GetFloat("maxFps");
bool fpsLimit = maxFps != 0 && i.current.outFps > maxFps;
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt("maxFpsMode") == 0;
if(!dontEncodeFullFpsVid)
await Encode(mode, path, outPath, i.current.outFps);
if (fpsLimit)
await Encode(mode, path, outPath.FilenameSuffix($"-{maxFps.ToStringDot("0.00")}fps"), i.current.outFps, maxFps);
}
catch (Exception e)
{
Logger.Log("FramesToVideo Error: " + e.Message, false);
MessageBox.Show("An error occured while trying to convert the interpolated frames to a video.\nCheck the log for details.");
}
}
static async Task CopyOutputFrames (string framesPath, string folderName)
{
Program.mainForm.SetStatus("Copying output frames...");
string copyPath = Path.Combine(i.current.outPath, folderName);
Logger.Log($"Moving output frames to '{copyPath}'");
IOUtils.TryDeleteIfExists(copyPath);
IOUtils.CreateDir(copyPath);
Stopwatch sw = new Stopwatch();
sw.Restart();
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{i.current.interpFactor}x.ini");
string[] vfrLines = IOUtils.ReadLines(vfrFile);
for (int idx = 1; idx <= vfrLines.Length; idx++)
{
string line = vfrLines[idx-1];
string inFilename = line.Split('/').Last().Remove("'").RemoveComments();
string framePath = Path.Combine(framesPath, inFilename);
string outFilename = Path.Combine(copyPath, idx.ToString().PadLeft(Padding.interpFrames, '0')) + Path.GetExtension(framePath);
if ((idx < vfrLines.Length) && vfrLines[idx].Contains(inFilename)) // If file is re-used in the next line, copy instead of move
File.Copy(framePath, outFilename);
else
File.Move(framePath, outFilename);
if (sw.ElapsedMilliseconds >= 500 || idx == vfrLines.Length)
{
sw.Restart();
Logger.Log($"Moving output frames to '{Path.GetFileName(copyPath)}' - {idx}/{vfrLines.Length}", false, true);
await Task.Delay(1);
}
}
}
static async Task Encode(i.OutMode mode, string framesPath, string outPath, float fps, float resampleFps = -1)
{
currentOutFile = outPath;
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{i.current.interpFactor}x.ini");
if (mode == i.OutMode.VidGif)
{
await FFmpegCommands.FramesToGifConcat(vfrFile, outPath, fps, true, Config.GetInt("gifColors"), resampleFps);
}
else
{
await FFmpegCommands.FramesToVideoConcat(vfrFile, outPath, mode, fps, resampleFps);
await MergeAudio(i.current.inPath, outPath);
int looptimes = GetLoopTimes();
if (looptimes > 0)
await Loop(currentOutFile, looptimes);
}
}
public static async Task ChunksToVideos(string tempFolder, string chunksFolder, string baseOutPath)
{
if (IOUtils.GetAmountOfFiles(chunksFolder, true, $"*{FFmpegUtils.GetExt(i.current.outMode)}") < 1)
{
i.Cancel("No video chunks found - An error must have occured during chunk encoding!", AiProcess.hasShownError);
return;
}
await Task.Delay(10);
Program.mainForm.SetStatus("Merging video chunks...");
try
{
DirectoryInfo chunksDir = new DirectoryInfo(chunksFolder);
foreach(DirectoryInfo dir in chunksDir.GetDirectories())
{
string suffix = dir.Name.Replace("chunks", "");
string tempConcatFile = Path.Combine(tempFolder, $"chunks-concat{suffix}.ini");
string concatFileContent = "";
foreach (string vid in IOUtils.GetFilesSorted(dir.FullName))
concatFileContent += $"file '{Paths.chunksDir}/{dir.Name}/{Path.GetFileName(vid)}'\n";
File.WriteAllText(tempConcatFile, concatFileContent);
Logger.Log($"CreateVideo: Running MergeChunks() for vfrFile '{Path.GetFileName(tempConcatFile)}'", true);
await MergeChunks(tempConcatFile, baseOutPath.FilenameSuffix(suffix));
}
}
catch (Exception e)
{
Logger.Log("ChunksToVideo Error: " + e.Message, false);
MessageBox.Show("An error occured while trying to merge the video chunks.\nCheck the log for details.");
}
}
static async Task MergeChunks(string vfrFile, string outPath)
{
await FFmpegCommands.ConcatVideos(vfrFile, outPath, -1);
await MergeAudio(i.current.inPath, outPath);
int looptimes = GetLoopTimes();
if (looptimes > 0)
await Loop(outPath, looptimes);
}
public static async Task EncodeChunk(string outPath, i.OutMode mode, int firstFrameNum, int framesAmount)
{
string vfrFileOriginal = Path.Combine(i.current.tempFolder, $"vfr-{i.current.interpFactor}x.ini");
string vfrFile = Path.Combine(i.current.tempFolder, $"vfr-chunk-{firstFrameNum}-{firstFrameNum + framesAmount}.ini");
File.WriteAllLines(vfrFile, IOUtils.ReadLines(vfrFileOriginal).Skip(firstFrameNum).Take(framesAmount));
float maxFps = Config.GetFloat("maxFps");
bool fpsLimit = maxFps != 0 && i.current.outFps > maxFps;
bool dontEncodeFullFpsVid = fpsLimit && Config.GetInt("maxFpsMode") == 0;
if(!dontEncodeFullFpsVid)
await FFmpegCommands.FramesToVideoConcat(vfrFile, outPath, mode, i.current.outFps, AvProcess.LogMode.Hidden, true); // Encode
if (fpsLimit)
{
string filename = Path.GetFileName(outPath);
string newParentDir = outPath.GetParentDir() + "-" + maxFps.ToStringDot("0.00") + "fps";
outPath = Path.Combine(newParentDir, filename);
await FFmpegCommands.FramesToVideoConcat(vfrFile, outPath, mode, i.current.outFps, maxFps, AvProcess.LogMode.Hidden, true); // Encode with limited fps
}
}
static async Task Loop(string outPath, int looptimes)
{
Logger.Log($"Looping {looptimes} {(looptimes == 1 ? "time" : "times")} to reach target length of {Config.GetInt("minOutVidLength")}s...");
await FFmpegCommands.LoopVideo(outPath, looptimes, Config.GetInt("loopMode") == 0);
}
static int GetLoopTimes()
{
int times = -1;
int minLength = Config.GetInt("minOutVidLength");
int minFrameCount = (minLength * i.current.outFps).RoundToInt();
int outFrames = i.currentInputFrameCount * i.current.interpFactor;
if (outFrames / i.current.outFps < minLength)
times = (int)Math.Ceiling((double)minFrameCount / (double)outFrames);
times--; // Not counting the 1st play (0 loops)
if (times <= 0) return -1; // Never try to loop 0 times, idk what would happen, probably nothing
return times;
}
public static async Task MergeAudio(string inputPath, string outVideo, int looptimes = -1)
{
if (!Config.GetBool("keepAudio")) return;
try
{
string audioFileBasePath = Path.Combine(i.current.tempFolder, "audio");
if (inputPath != null && IOUtils.IsPathDirectory(inputPath) && !File.Exists(IOUtils.GetAudioFile(audioFileBasePath))) // Try loading out of same folder as input if input is a folder
audioFileBasePath = Path.Combine(i.current.tempFolder.GetParentDir(), "audio");
if (!File.Exists(IOUtils.GetAudioFile(audioFileBasePath)))
await FFmpegCommands.ExtractAudio(inputPath, audioFileBasePath); // Extract from sourceVideo to audioFile unless it already exists
if (!File.Exists(IOUtils.GetAudioFile(audioFileBasePath)) || new FileInfo(IOUtils.GetAudioFile(audioFileBasePath)).Length < 4096)
{
Logger.Log("No compatible audio stream found.", true);
return;
}
await FFmpegCommands.MergeAudioAndSubs(outVideo, IOUtils.GetAudioFile(audioFileBasePath), i.current.tempFolder); // Merge from audioFile into outVideo
}
catch (Exception e)
{
Logger.Log("Failed to copy audio!");
Logger.Log("MergeAudio() Exception: " + e.Message, true);
}
}
}
}

160
Code5/Main/FrameOrder.cs Normal file
View File

@@ -0,0 +1,160 @@
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.UI;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Main
{
class FrameOrder
{
public enum Mode { CFR, VFR }
public static int timebase = 10000;
public static async Task CreateFrameOrderFile(string framesPath, bool loopEnabled, int times)
{
Logger.Log("Generating frame order information...");
try
{
await CreateEncFile(framesPath, loopEnabled, times, false);
Logger.Log($"Generating frame order information... Done.", false, true);
}
catch (Exception e)
{
Logger.Log($"Error generating frame order information: {e.Message}");
}
}
static Dictionary<string, int> dupesDict = new Dictionary<string, int>();
static void LoadDupesFile (string path)
{
dupesDict.Clear();
if (!File.Exists(path)) return;
string[] dupesFileLines = IOUtils.ReadLines(path);
foreach(string line in dupesFileLines)
{
string[] values = line.Split(':');
dupesDict.Add(values[0], values[1].GetInt());
}
}
public static async Task CreateEncFile (string framesPath, bool loopEnabled, int interpFactor, bool notFirstRun)
{
if (Interpolate.canceled) return;
Logger.Log($"Generating frame order information for {interpFactor}x...", false, true);
bool loop = Config.GetBool("enableLoop");
bool sceneDetection = true;
string ext = InterpolateUtils.GetOutExt();
FileInfo[] frameFiles = new DirectoryInfo(framesPath).GetFiles($"*.png");
string vfrFile = Path.Combine(framesPath.GetParentDir(), $"vfr-{interpFactor}x.ini");
string fileContent = "";
string dupesFile = Path.Combine(framesPath.GetParentDir(), $"dupes.ini");
LoadDupesFile(dupesFile);
string scnFramesPath = Path.Combine(framesPath.GetParentDir(), Paths.scenesDir);
string interpPath = Paths.interpDir;
List<string> sceneFrames = new List<string>();
if (Directory.Exists(scnFramesPath))
sceneFrames = Directory.GetFiles(scnFramesPath).Select(file => Path.GetFileNameWithoutExtension(file)).ToList();
bool debug = Config.GetBool("frameOrderDebug", false);
int totalFileCount = 0;
for (int i = 0; i < (frameFiles.Length - 1); i++)
{
if (Interpolate.canceled) return;
int interpFramesAmount = interpFactor;
string inputFilenameNoExt = Path.GetFileNameWithoutExtension(frameFiles[i].Name);
int dupesAmount = dupesDict.ContainsKey(inputFilenameNoExt) ? dupesDict[inputFilenameNoExt] : 0;
if(debug) Logger.Log($"{Path.GetFileNameWithoutExtension(frameFiles[i].Name)} has {dupesAmount} dupes", true);
bool discardThisFrame = (sceneDetection && (i + 2) < frameFiles.Length && sceneFrames.Contains(Path.GetFileNameWithoutExtension(frameFiles[i + 1].Name))); // i+2 is in scene detection folder, means i+1 is ugly interp frame
// If loop is enabled, account for the extra frame added to the end for loop continuity
if (loopEnabled && i == (frameFiles.Length - 2))
interpFramesAmount = interpFramesAmount * 2;
if (debug) Logger.Log($"Writing out frames for in frame {i} which has {dupesAmount} dupes", true);
// Generate frames file lines
for (int frm = 0; frm < interpFramesAmount; frm++)
{
//if (debug) Logger.Log($"Writing out frame {frm+1}/{interpFramesAmount}", true);
if (discardThisFrame) // If frame is scene cut frame
{
//if (debug) Logger.Log($"Writing frame {totalFileCount} [Discarding Next]", true);
totalFileCount++;
int lastNum = totalFileCount;
fileContent = WriteFrameWithDupes(dupesAmount, fileContent, totalFileCount, interpPath, ext, debug, $"[In: {inputFilenameNoExt}] [{((frm == 0) ? " Source " : $"Interp {frm}")}] [DiscardNext]");
//if (debug) Logger.Log("Discarding interp frames with out num " + totalFileCount);
for (int dupeCount = 1; dupeCount < interpFramesAmount; dupeCount++)
{
totalFileCount++;
if (debug) Logger.Log($"Writing frame {totalFileCount} which is actually repeated frame {lastNum}", true);
fileContent = WriteFrameWithDupes(dupesAmount, fileContent, lastNum, interpPath, ext, debug, $"[In: {inputFilenameNoExt}] [DISCARDED]");
}
frm = interpFramesAmount;
}
else
{
totalFileCount++;
fileContent = WriteFrameWithDupes(dupesAmount, fileContent, totalFileCount, interpPath, ext, debug, $"[In: {inputFilenameNoExt}] [{((frm == 0) ? " Source " : $"Interp {frm}")}]");
}
}
if ((i + 1) % 100 == 0)
await Task.Delay(1);
}
// if(debug) Logger.Log("target: " + ((frameFiles.Length * interpFactor) - (interpFactor - 1)), true);
// if(debug) Logger.Log("totalFileCount: " + totalFileCount, true);
totalFileCount++;
fileContent += $"file '{interpPath}/{totalFileCount.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'\n";
string finalFileContent = fileContent.Trim();
if(loop)
finalFileContent = finalFileContent.Remove(finalFileContent.LastIndexOf("\n"));
File.WriteAllText(vfrFile, finalFileContent);
if (notFirstRun) return; // Skip all steps that only need to be done once
if (loop)
{
int lastFileNumber = frameFiles.Last().Name.GetInt() + 1;
string loopFrameTargetPath = Path.Combine(frameFiles.First().FullName.GetParentDir(), lastFileNumber.ToString().PadLeft(Padding.inputFrames, '0') + $".png");
if (File.Exists(loopFrameTargetPath))
{
if (debug) Logger.Log($"Won't copy loop frame - {Path.GetFileName(loopFrameTargetPath)} already exists.", true);
return;
}
File.Copy(frameFiles.First().FullName, loopFrameTargetPath);
if (debug) Logger.Log($"Copied loop frame to {loopFrameTargetPath}.", true);
}
}
static string WriteFrameWithDupes (int dupesAmount, string fileContent, int frameNum, string interpPath, string ext, bool debug, string note = "")
{
for (int writtenDupes = -1; writtenDupes < dupesAmount; writtenDupes++) // Write duplicates
{
if (debug) Logger.Log($"Writing frame {frameNum} (writtenDupes {writtenDupes})", true, false);
fileContent += $"file '{interpPath}/{frameNum.ToString().PadLeft(Padding.interpFrames, '0')}.{ext}'{(debug ? ($" # Dupe {(writtenDupes+1).ToString("000")} {note}").Replace("Dupe 000", " ") : "" )}\n";
}
return fileContent;
}
}
}

216
Code5/Main/Interpolate.cs Normal file
View File

@@ -0,0 +1,216 @@
using Flowframes;
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.Magick;
using Flowframes.Main;
using Flowframes.MiscUtils;
using Flowframes.OS;
using Flowframes.UI;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Padding = Flowframes.Data.Padding;
using Utils = Flowframes.Main.InterpolateUtils;
namespace Flowframes
{
public class Interpolate
{
public enum OutMode { VidMp4, VidMkv, VidWebm, VidProRes, VidAvi, VidGif, ImgPng }
public static int currentInputFrameCount;
public static bool currentlyUsingAutoEnc;
public static InterpSettings current;
public static bool canceled = false;
static Stopwatch sw = new Stopwatch();
public static async Task Start()
{
canceled = false;
if (!Utils.InputIsValid(current.inPath, current.outPath, current.outFps, current.interpFactor, current.outMode)) return; // General input checks
if (!Utils.CheckAiAvailable(current.ai)) return; // Check if selected AI pkg is installed
if (!Utils.CheckDeleteOldTempFolder()) return; // Try to delete temp folder if an old one exists
if(!Utils.CheckPathValid(current.inPath)) return; // Check if input path/file is valid
Utils.PathAsciiCheck(current.inPath, current.outPath);
currentInputFrameCount = await Utils.GetInputFrameCountAsync(current.inPath);
Program.mainForm.SetStatus("Starting...");
Program.mainForm.SetWorking(true);
await GetFrames();
if (canceled) return;
sw.Restart();
await PostProcessFrames();
if (canceled) return;
await RunAi(current.interpFolder, current.ai);
if (canceled) return;
Program.mainForm.SetProgress(100);
if(!currentlyUsingAutoEnc)
await CreateVideo.Export(current.interpFolder, current.outFilename, current.outMode);
IOUtils.ReverseRenaming(AiProcess.filenameMap, true); // Get timestamps back
Cleanup(current.interpFolder);
Program.mainForm.SetWorking(false);
Logger.Log("Total processing time: " + FormatUtils.Time(sw.Elapsed));
sw.Stop();
Program.mainForm.SetStatus("Done interpolating!");
}
public static async Task GetFrames ()
{
if (!current.inputIsFrames) // Input is video - extract frames first
{
current.alpha = (Config.GetBool("enableAlpha", false) && (Path.GetExtension(current.inPath).ToLower() == ".gif"));
await ExtractFrames(current.inPath, current.framesFolder, current.alpha);
}
else
{
current.alpha = (Config.GetBool("enableAlpha", false) && Path.GetExtension(IOUtils.GetFilesSorted(current.inPath).First()).ToLower() == ".gif");
await FFmpegCommands.ImportImages(current.inPath, current.framesFolder, current.alpha, await Utils.GetOutputResolution(current.inPath, true));
}
}
public static async Task ExtractFrames(string inPath, string outPath, bool alpha, bool allowSceneDetect = true, bool extractAudio = true)
{
if (Config.GetBool("scnDetect"))
{
Program.mainForm.SetStatus("Extracting scenes from video...");
await FFmpegCommands.ExtractSceneChanges(inPath, Path.Combine(current.tempFolder, Paths.scenesDir), current.inFps);
await Task.Delay(10);
}
Program.mainForm.SetStatus("Extracting frames from video...");
bool mpdecimate = Config.GetInt("dedupMode") == 2;
await FFmpegCommands.VideoToFrames(inPath, outPath, alpha, current.inFps, mpdecimate, false, await Utils.GetOutputResolution(inPath, true), false);
if (mpdecimate)
{
int framesLeft = IOUtils.GetAmountOfFiles(outPath, false, $"*.png");
int framesDeleted = currentInputFrameCount - framesLeft;
float percentDeleted = ((float)framesDeleted / currentInputFrameCount) * 100f;
string keptPercent = $"{(100f - percentDeleted).ToString("0.0")}%";
Logger.Log($"[Deduplication] Kept {framesLeft} ({keptPercent}) frames, deleted {framesDeleted} frames.");
}
if(!Config.GetBool("allowConsecutiveSceneChanges", true))
Utils.FixConsecutiveSceneFrames(Path.Combine(current.tempFolder, Paths.scenesDir), current.framesFolder);
if (extractAudio)
{
string audioFile = Path.Combine(current.tempFolder, "audio");
if (audioFile != null && !File.Exists(audioFile))
await FFmpegCommands.ExtractAudio(inPath, audioFile);
}
await FFmpegCommands.ExtractSubtitles(inPath, current.tempFolder, current.outMode);
}
public static async Task PostProcessFrames (bool sbsMode = false)
{
if (canceled) return;
int extractedFrames = IOUtils.GetAmountOfFiles(current.framesFolder, false, "*.png");
if (!Directory.Exists(current.framesFolder) || currentInputFrameCount <= 0 || extractedFrames < 2)
{
if(extractedFrames == 1)
Cancel("Only a single frame was extracted from your input file!\n\nPossibly your input is an image, not a video?");
else
Cancel("Frame extraction failed!\n\nYour input file might be incompatible.");
}
if (Config.GetInt("dedupMode") == 1)
await Dedupe.Run(current.framesFolder);
else
Dedupe.ClearCache();
if(!Config.GetBool("enableLoop"))
await Utils.CopyLastFrame(currentInputFrameCount);
if (Config.GetInt("dedupMode") > 0)
await Dedupe.CreateDupesFile(current.framesFolder, currentInputFrameCount);
if (canceled) return;
if(current.alpha)
await Converter.ExtractAlpha(current.framesFolder, current.framesFolder + "-a");
await FrameOrder.CreateFrameOrderFile(current.framesFolder, Config.GetBool("enableLoop"), current.interpFactor);
if (canceled) return;
AiProcess.filenameMap = IOUtils.RenameCounterDirReversible(current.framesFolder, "png", 1, 8);
}
public static async Task RunAi(string outpath, AI ai, bool stepByStep = false)
{
Program.mainForm.SetStatus("Downloading models...");
await ModelDownloader.DownloadModelFiles(Path.GetFileNameWithoutExtension(ai.pkg.fileName), current.model);
if (canceled) return;
currentlyUsingAutoEnc = Utils.CanUseAutoEnc(stepByStep, current);
IOUtils.CreateDir(outpath);
List<Task> tasks = new List<Task>();
if (ai.aiName == Networks.rifeCuda.aiName)
tasks.Add(AiProcess.RunRifeCuda(current.framesFolder, current.interpFactor, current.model));
if (ai.aiName == Networks.rifeNcnn.aiName)
tasks.Add(AiProcess.RunRifeNcnn(current.framesFolder, outpath, current.interpFactor, current.model));
if (ai.aiName == Networks.dainNcnn.aiName)
tasks.Add(AiProcess.RunDainNcnn(current.framesFolder, outpath, current.interpFactor, current.model, Config.GetInt("dainNcnnTilesize", 512)));
if (currentlyUsingAutoEnc)
{
Logger.Log($"{Logger.GetLastLine()} (Using Auto-Encode)", true);
tasks.Add(AutoEncode.MainLoop(outpath));
}
Program.mainForm.SetStatus("Running AI...");
await Task.WhenAll(tasks);
}
public static void Cancel(string reason = "", bool noMsgBox = false)
{
if (AiProcess.currentAiProcess != null && !AiProcess.currentAiProcess.HasExited)
OSUtils.KillProcessTree(AiProcess.currentAiProcess.Id);
if (AvProcess.lastProcess != null && !AvProcess.lastProcess.HasExited)
OSUtils.KillProcessTree(AvProcess.lastProcess.Id);
canceled = true;
Program.mainForm.SetStatus("Canceled.");
Program.mainForm.SetProgress(0);
if (Config.GetInt("processingMode") == 0 && !Config.GetBool("keepTempFolder"))
IOUtils.TryDeleteIfExists(current.tempFolder);
AutoEncode.busy = false;
Program.mainForm.SetWorking(false);
Program.mainForm.SetTab("interpolation");
if(!Logger.GetLastLine().Contains("Canceled interpolation."))
Logger.Log("Canceled interpolation.");
if (!string.IsNullOrWhiteSpace(reason) && !noMsgBox)
Utils.ShowMessage($"Canceled:\n\n{reason}");
}
public static void Cleanup(string interpFramesDir, bool ignoreKeepSetting = false)
{
if (!ignoreKeepSetting && Config.GetBool("keepTempFolder")) return;
Logger.Log("Deleting temporary files...");
try
{
if (Config.GetBool("keepFrames"))
IOUtils.Copy(interpFramesDir, Path.Combine(current.tempFolder.GetParentDir(), Path.GetFileName(current.tempFolder).Replace("-temp", "-interpframes")));
Directory.Delete(current.tempFolder, true);
}
catch (Exception e)
{
Logger.Log("Cleanup Error: " + e.Message, true);
}
}
}
}

View File

@@ -0,0 +1,142 @@
using Flowframes.AudioVideo;
using Flowframes.Data;
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.Main
{
using static Interpolate;
class InterpolateSteps
{
public enum Step { ExtractScnChanges, ExtractFrames, Interpolate, CreateVid, Reset }
//public static string current.inPath;
//public static string currentOutPath;
//public static string current.interpFolder;
//public static AI currentAi;
//public static OutMode currentOutMode;
public static async Task Run(string step)
{
Logger.Log($"[SBS] Running step '{step}'", true);
canceled = false;
Program.mainForm.SetWorking(true);
current = Program.mainForm.GetCurrentSettings();
if (!InterpolateUtils.InputIsValid(current.inPath, current.outPath, current.outFps, current.interpFactor, current.outMode)) return; // General input checks
if (step.Contains("Extract Scene Changes"))
{
if (!current.inputIsFrames) // Input is video - extract frames first
await ExtractSceneChanges();
else
InterpolateUtils.ShowMessage("Scene changes can only be extracted from videos, not frames!", "Error");
}
if (step.Contains("Extract Frames"))
{
await GetFrames();
}
if (step.Contains("Run Interpolation"))
await DoInterpolate();
if (step.Contains("Export"))
await CreateOutputVid();
if (step.Contains("Cleanup"))
await Reset();
Program.mainForm.SetWorking(false);
Logger.Log("Done running this step.");
}
public static async Task ExtractSceneChanges()
{
string scenesPath = Path.Combine(current.tempFolder, Paths.scenesDir);
if (!IOUtils.TryDeleteIfExists(scenesPath))
{
InterpolateUtils.ShowMessage("Failed to delete existing scenes folder - Make sure no file is opened in another program!", "Error");
return;
}
Program.mainForm.SetStatus("Extracting scenes from video...");
await FFmpegCommands.ExtractSceneChanges(current.inPath, scenesPath, current.inFps);
await Task.Delay(10);
}
public static async Task ExtractVideoFrames()
{
if (!IOUtils.TryDeleteIfExists(current.framesFolder))
{
InterpolateUtils.ShowMessage("Failed to delete existing frames folder - Make sure no file is opened in another program!", "Error");
return;
}
currentInputFrameCount = await InterpolateUtils.GetInputFrameCountAsync(current.inPath);
AiProcess.filenameMap.Clear();
await ExtractFrames(current.inPath, current.framesFolder, false, true);
}
public static async Task DoInterpolate()
{
current.framesFolder = Path.Combine(current.tempFolder, Paths.framesDir);
if (!Directory.Exists(current.framesFolder) || IOUtils.GetAmountOfFiles(current.framesFolder, false, "*.png") < 2)
{
InterpolateUtils.ShowMessage("There are no extracted frames that can be interpolated!\nDid you run the extraction step?", "Error");
return;
}
if (!IOUtils.TryDeleteIfExists(current.interpFolder))
{
InterpolateUtils.ShowMessage("Failed to delete existing frames folder - Make sure no file is opened in another program!", "Error");
return;
}
currentInputFrameCount = await InterpolateUtils.GetInputFrameCountAsync(current.inPath);
foreach (string ini in Directory.GetFiles(current.tempFolder, "*.ini", SearchOption.TopDirectoryOnly))
IOUtils.TryDeleteIfExists(ini);
IOUtils.ReverseRenaming(AiProcess.filenameMap, true); // Get timestamps back
// TODO: Check if this works lol, remove if it does
//if (Config.GetBool("sbsAllowAutoEnc"))
// nextOutPath = Path.Combine(currentOutPath, Path.GetFileNameWithoutExtension(current.inPath) + IOUtils.GetAiSuffix(current.ai, current.interpFactor) + InterpolateUtils.GetExt(current.outMode));
await PostProcessFrames(true);
int frames = IOUtils.GetAmountOfFiles(current.framesFolder, false, "*.png");
int targetFrameCount = frames * current.interpFactor;
if (canceled) return;
Program.mainForm.SetStatus("Running AI...");
await RunAi(current.interpFolder, current.ai, true);
Program.mainForm.SetProgress(0);
}
public static async Task CreateOutputVid()
{
string[] outFrames = IOUtils.GetFilesSorted(current.interpFolder, $"*.{InterpolateUtils.GetOutExt()}");
if (outFrames.Length > 0 && !IOUtils.CheckImageValid(outFrames[0]))
{
InterpolateUtils.ShowMessage("Invalid frame files detected!\n\nIf you used Auto-Encode, this is normal, and you don't need to run " +
"this step as the video was already created in the \"Interpolate\" step.", "Error");
return;
}
string outPath = Path.Combine(current.outPath, Path.GetFileNameWithoutExtension(current.inPath) + IOUtils.GetCurrentExportSuffix() + FFmpegUtils.GetExt(current.outMode));
await CreateVideo.Export(current.interpFolder, outPath, current.outMode);
}
public static async Task Reset()
{
Cleanup(current.interpFolder, true);
}
}
}

View File

@@ -0,0 +1,444 @@
using Flowframes.Data;
using Flowframes.Forms;
using Flowframes.IO;
using Flowframes.MiscUtils;
using Flowframes.OS;
using Flowframes.UI;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using i = Flowframes.Interpolate;
using Padding = Flowframes.Data.Padding;
namespace Flowframes.Main
{
class InterpolateUtils
{
public static PictureBox preview;
public static BigPreviewForm bigPreviewForm;
public static async Task CopyLastFrame (int lastFrameNum)
{
try
{
lastFrameNum--; // We have to do this as extracted frames start at 0, not 1
bool frameFolderInput = IOUtils.IsPathDirectory(i.current.inPath);
string targetPath = Path.Combine(i.current.framesFolder, lastFrameNum.ToString().PadLeft(Padding.inputFrames, '0') + ".png");
if (File.Exists(targetPath)) return;
Size res = IOUtils.GetImage(IOUtils.GetFilesSorted(i.current.framesFolder, false).First()).Size;
if (frameFolderInput)
{
string lastFramePath = IOUtils.GetFilesSorted(i.current.inPath, false).Last();
await FFmpegCommands.ExtractLastFrame(lastFramePath, targetPath, res);
}
else
{
await FFmpegCommands.ExtractLastFrame(i.current.inPath, targetPath, res);
}
}
catch (Exception e)
{
Logger.Log("CopyLastFrame Error: " + e.Message);
}
}
public static string GetOutExt (bool withDot = false)
{
string dotStr = withDot ? "." : "";
if (Config.GetBool("jpegInterp"))
return dotStr + "jpg";
return dotStr + "png";
}
public static int targetFrames;
public static string currentOutdir;
public static int currentFactor;
public static bool progressPaused = false;
public static bool progCheckRunning = false;
public static async void GetProgressByFrameAmount(string outdir, int target)
{
progCheckRunning = true;
targetFrames = target;
currentOutdir = outdir;
Logger.Log($"Starting GetProgressByFrameAmount() loop for outdir '{currentOutdir}', target is {target} frames", true);
bool firstProgUpd = true;
Program.mainForm.SetProgress(0);
while (Program.busy)
{
if (!progressPaused && AiProcess.processTime.IsRunning && Directory.Exists(currentOutdir))
{
if (firstProgUpd && Program.mainForm.IsInFocus())
Program.mainForm.SetTab("preview");
firstProgUpd = false;
string[] frames = IOUtils.GetFilesSorted(currentOutdir, $"*.{GetOutExt()}");
if (frames.Length > 1)
UpdateInterpProgress(frames.Length, targetFrames, frames[frames.Length - 1]);
if (frames.Length >= targetFrames)
break;
await Task.Delay(GetProgressWaitTime(frames.Length));
}
else
{
await Task.Delay(200);
}
}
progCheckRunning = false;
if (i.canceled)
Program.mainForm.SetProgress(0);
}
public static void UpdateInterpProgress(int frames, int target, string latestFramePath = "")
{
if (i.canceled) return;
frames = frames.Clamp(0, target);
int percent = (int)Math.Round(((float)frames / target) * 100f);
Program.mainForm.SetProgress(percent);
float generousTime = ((AiProcess.processTime.ElapsedMilliseconds - AiProcess.lastStartupTimeMs) / 1000f);
float fps = (float)frames / generousTime;
string fpsIn = (fps / currentFactor).ToString("0.00");
string fpsOut = fps.ToString("0.00");
float secondsPerFrame = generousTime / (float)frames;
int framesLeft = target - frames;
float eta = framesLeft * secondsPerFrame;
string etaStr = FormatUtils.Time(new TimeSpan(0, 0, eta.RoundToInt()), false);
bool replaceLine = Regex.Split(Logger.textbox.Text, "\r\n|\r|\n").Last().Contains("Average Speed: ");
string logStr = $"Interpolated {frames}/{target} frames ({percent}%) - Average Speed: {fpsIn} FPS In / {fpsOut} FPS Out - ";
logStr += $"Time: {FormatUtils.Time(AiProcess.processTime.Elapsed)} - ETA: {etaStr}";
if (AutoEncode.busy) logStr += " - Encoding...";
Logger.Log(logStr, false, replaceLine);
try
{
if (!string.IsNullOrWhiteSpace(latestFramePath) && frames > currentFactor)
{
if (bigPreviewForm == null && !preview.Visible /* ||Program.mainForm.WindowState != FormWindowState.Minimized */ /* || !Program.mainForm.IsInFocus()*/) return; // Skip if the preview is not visible or the form is not in focus
Image img = IOUtils.GetImage(latestFramePath);
SetPreviewImg(img);
}
}
catch { }
}
public static void SetPreviewImg (Image img)
{
if (img == null)
return;
preview.Image = img;
if (bigPreviewForm != null)
bigPreviewForm.SetImage(img);
}
public static Dictionary<string, int> frameCountCache = new Dictionary<string, int>();
public static async Task<int> GetInputFrameCountAsync (string path)
{
string hash = await IOUtils.GetHashAsync(path, IOUtils.Hash.xxHash); // Get checksum for caching
if (hash.Length > 1 && frameCountCache.ContainsKey(hash))
{
Logger.Log($"FrameCountCache contains this hash ({hash}), using cached frame count.", true);
return frameCountCache[hash];
}
else
{
Logger.Log($"Hash ({hash}) not cached, reading frame count.", true);
}
int frameCount = 0;
if (IOUtils.IsPathDirectory(path))
frameCount = IOUtils.GetAmountOfFiles(path, false);
else
frameCount = await FFmpegCommands.GetFrameCountAsync(path);
if (hash.Length > 1 && frameCount > 5000) // Cache if >5k frames to avoid re-reading it every single time
{
Logger.Log($"Adding hash ({hash}) with frame count {frameCount} to cache.", true);
frameCountCache[hash] = frameCount; // Use CRC32 instead of path to avoid using cached value if file was changed
}
return frameCount;
}
public static int GetProgressWaitTime(int numFrames)
{
float hddMultiplier = !Program.lastInputPathIsSsd ? 2f : 1f;
int waitMs = 200;
if (numFrames > 100)
waitMs = 500;
if (numFrames > 1000)
waitMs = 1000;
if (numFrames > 2500)
waitMs = 1500;
if (numFrames > 5000)
waitMs = 2500;
return (waitMs * hddMultiplier).RoundToInt();
}
public static string GetTempFolderLoc (string inPath, string outPath)
{
string basePath = inPath.GetParentDir();
if(Config.GetInt("tempFolderLoc") == 1)
basePath = outPath.GetParentDir();
if (Config.GetInt("tempFolderLoc") == 2)
basePath = outPath;
if (Config.GetInt("tempFolderLoc") == 3)
basePath = IOUtils.GetExeDir();
if (Config.GetInt("tempFolderLoc") == 4)
{
string custPath = Config.Get("tempDirCustom");
if(IOUtils.IsDirValid(custPath))
basePath = custPath;
}
return Path.Combine(basePath, Path.GetFileNameWithoutExtension(inPath).StripBadChars().Remove(" ").Trunc(30, false) + "-temp");
}
public static bool InputIsValid(string inDir, string outDir, float fpsOut, int interp, Interpolate.OutMode outMode)
{
bool passes = true;
bool isFile = !IOUtils.IsPathDirectory(inDir);
if ((passes && isFile && !IOUtils.IsFileValid(inDir)) || (!isFile && !IOUtils.IsDirValid(inDir)))
{
ShowMessage("Input path is not valid!");
passes = false;
}
if (passes && !IOUtils.IsDirValid(outDir))
{
ShowMessage("Output path is not valid!");
passes = false;
}
if (passes && interp != 2 && interp != 4 && interp != 8)
{
ShowMessage("Interpolation factor is not valid!");
passes = false;
}
if (passes && outMode == i.OutMode.VidGif && fpsOut > 50)
{
ShowMessage("Invalid output frame rate!\nGIF does not properly support frame rates above 40 FPS.\nPlease use MP4, WEBM or another video format.");
passes = false;
}
if (passes && fpsOut < 1 || fpsOut > 500)
{
ShowMessage("Invalid output frame rate - Must be 1-500.");
passes = false;
}
if (!passes)
i.Cancel("Invalid settings detected.", true);
return passes;
}
public static void PathAsciiCheck (string inpath, string outpath)
{
bool shownMsg = false;
if (OSUtils.HasNonAsciiChars(inpath))
{
ShowMessage("Warning: Input path includes non-ASCII characters. This might cause problems.");
shownMsg = true;
}
if (!shownMsg && OSUtils.HasNonAsciiChars(outpath))
ShowMessage("Warning: Output path includes non-ASCII characters. This might cause problems.");
}
public static void GifCompatCheck (Interpolate.OutMode outMode, float fpsOut, int targetFrameCount)
{
if (outMode != Interpolate.OutMode.VidGif)
return;
if(fpsOut >= 50f)
Logger.Log("Warning: GIFs above 50 FPS might play slower on certain software/hardware! MP4 is recommended for higher frame rates.");
int maxGifFrames = 200;
if (targetFrameCount > maxGifFrames)
{
ShowMessage($"You can't use GIF with more than {maxGifFrames} output frames!\nPlease use MP4 for this.", "Error");
i.Cancel($"Can't use GIF encoding with more than {maxGifFrames} frames!");
}
}
public static bool CheckAiAvailable (AI ai)
{
if (!PkgUtils.IsAiAvailable(ai))
{
ShowMessage("The selected AI is not installed!\nYou can download it from the Package Installer.", "Error");
i.Cancel("Selected AI not available.", true);
return false;
}
return true;
}
public static bool CheckDeleteOldTempFolder ()
{
if (!IOUtils.TryDeleteIfExists(i.current.tempFolder))
{
ShowMessage("Failed to remove an existing temp folder of this video!\nMake sure you didn't open any frames in an editor.", "Error");
i.Cancel();
return false;
}
return true;
}
public static bool CheckPathValid (string path)
{
if (IOUtils.IsPathDirectory(path))
{
if (!IOUtils.IsDirValid(path))
{
ShowMessage("Input directory is not valid.");
i.Cancel();
return false;
}
}
else
{
if (!IsVideoValid(path))
{
ShowMessage("Input video file is not valid.");
return false;
}
}
return true;
}
public static bool IsVideoValid(string videoPath)
{
if (videoPath == null || !IOUtils.IsFileValid(videoPath))
return false;
// string ext = Path.GetExtension(videoPath).ToLower();
// if (!Formats.supported.Contains(ext))
// return false;
return true;
}
public static void ShowMessage(string msg, string title = "Message")
{
if (!BatchProcessing.busy)
MessageBox.Show(msg, title);
Logger.Log("Message: " + msg, true);
}
public static async Task<Size> GetOutputResolution (string inputPath, bool print)
{
Size resolution = await IOUtils.GetVideoOrFramesRes(inputPath);
return GetOutputResolution(resolution, print);
}
public static Size GetOutputResolution(Size inputRes, bool print = false)
{
int maxHeight = RoundDiv2(Config.GetInt("maxVidHeight"));
if (inputRes.Height > maxHeight)
{
float factor = (float)maxHeight / inputRes.Height;
Logger.Log($"Un-rounded downscaled size: {(inputRes.Width * factor).ToString("0.00")}x{Config.GetInt("maxVidHeight")}", true);
int width = RoundDiv2((inputRes.Width * factor).RoundToInt());
if (print)
Logger.Log($"Video is bigger than the maximum - Downscaling to {width}x{maxHeight}.");
return new Size(width, maxHeight);
}
else
{
return new Size(RoundDiv2(inputRes.Width), RoundDiv2(inputRes.Height));
}
}
public static int RoundDiv2(int n) // Round to a number that's divisible by 2 (for h264 etc)
{
int a = (n / 2) * 2; // Smaller multiple
int b = a + 2; // Larger multiple
return (n - a > b - n) ? b : a; // Return of closest of two
}
public static bool CanUseAutoEnc (bool stepByStep, InterpSettings current)
{
AutoEncode.UpdateChunkAndBufferSizes();
if (current.alpha)
{
Logger.Log($"Not Using AutoEnc: Alpha mode is enabled.", true);
return false;
}
if (!current.outMode.ToString().ToLower().Contains("vid") || current.outMode.ToString().ToLower().Contains("gif"))
{
Logger.Log($"Not Using AutoEnc: Out Mode is not video ({current.outMode.ToString()})", true);
return false;
}
if(stepByStep && !Config.GetBool("sbsAllowAutoEnc"))
{
Logger.Log($"Not Using AutoEnc: Using step-by-step mode, but 'sbsAllowAutoEnc' is false.", true);
return false;
}
if (!stepByStep && Config.GetInt("autoEncMode") == 0)
{
Logger.Log($"Not Using AutoEnc: 'autoEncMode' is 0.", true);
return false;
}
int inFrames = IOUtils.GetAmountOfFiles(current.framesFolder, false);
if (inFrames * current.interpFactor < (AutoEncode.chunkSize + AutoEncode.safetyBufferFrames) * 1.2f)
{
Logger.Log($"Not Using AutoEnc: Input frames ({inFrames}) * factor ({current.interpFactor}) is smaller than (chunkSize ({AutoEncode.chunkSize}) + safetyBufferFrames ({AutoEncode.safetyBufferFrames}) * 1.2f)", true);
return false;
}
return true;
}
public static async Task<bool> UseUHD ()
{
return (await GetOutputResolution(i.current.inPath, false)).Height >= Config.GetInt("uhdThresh");
}
public static void FixConsecutiveSceneFrames (string sceneFramesPath, string sourceFramesPath)
{
if (!Directory.Exists(sceneFramesPath) || IOUtils.GetAmountOfFiles(sceneFramesPath, false) < 1)
return;
List<string> sceneFrames = IOUtils.GetFilesSorted(sceneFramesPath).Select(x => Path.GetFileNameWithoutExtension(x)).ToList();
List<string> sourceFrames = IOUtils.GetFilesSorted(sourceFramesPath).Select(x => Path.GetFileNameWithoutExtension(x)).ToList();
List<string> sceneFramesToDelete = new List<string>();
foreach(string scnFrame in sceneFrames)
{
if (sceneFramesToDelete.Contains(scnFrame))
continue;
int sourceIndexForScnFrame = sourceFrames.IndexOf(scnFrame); // Get source index of scene frame
if ((sourceIndexForScnFrame + 1) == sourceFrames.Count)
continue;
string followingFrame = sourceFrames[sourceIndexForScnFrame + 1]; // Get filename/timestamp of the next source frame
if (sceneFrames.Contains(followingFrame)) // If next source frame is in scene folder, add to deletion list
sceneFramesToDelete.Add(followingFrame);
}
foreach (string frame in sceneFramesToDelete)
IOUtils.TryDeleteIfExists(Path.Combine(sceneFramesPath, frame + ".png"));
}
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.MiscUtils
{
class Benchmarker
{
static Stopwatch sw = new Stopwatch();
public static void Start ()
{
sw.Restart();
}
public static string GetTimeStr (bool stop)
{
if (stop)
sw.Stop();
return FormatUtils.TimeSw(sw);
}
public static TimeSpan GetTime(bool stop)
{
if (stop)
sw.Stop();
return sw.Elapsed;
}
public static long GetTimeMs(bool stop)
{
if (stop)
sw.Stop();
return sw.ElapsedMilliseconds;
}
}
}

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.MiscUtils
{
class FormatUtils
{
public static string Bytes(long sizeBytes)
{
try
{
string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
if (sizeBytes == 0)
return "0" + suf[0];
long bytes = Math.Abs(sizeBytes);
int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
double num = Math.Round(bytes / Math.Pow(1024, place), 1);
return ($"{Math.Sign(sizeBytes) * num} {suf[place]}");
}
catch
{
return "N/A B";
}
}
public static string Time(long milliseconds)
{
double secs = (milliseconds / 1000f);
if (milliseconds <= 1000)
{
return milliseconds + "ms";
}
return secs.ToString("0.00") + "s";
}
public static string Time (TimeSpan span, bool allowMs = true)
{
if(span.TotalHours >= 1f)
return span.ToString(@"hh\:mm\:ss");
if (span.TotalMinutes >= 1f)
return span.ToString(@"mm\:ss");
if (span.TotalSeconds >= 1f || !allowMs)
return span.ToString(@"ss".TrimStart('0').PadLeft(1, '0')) + "s";
return span.ToString(@"fff").TrimStart('0').PadLeft(1, '0') + "ms";
}
public static string TimeSw(Stopwatch sw)
{
long elapsedMs = sw.ElapsedMilliseconds;
return Time(elapsedMs);
}
public static string Ratio(long numFrom, long numTo)
{
float ratio = ((float)numTo / (float)numFrom) * 100f;
return ratio.ToString("0.00") + "%";
}
public static string RatioInt(long numFrom, long numTo)
{
double ratio = Math.Round(((float)numTo / (float)numFrom) * 100f);
return ratio + "%";
}
public static string ConcatStrings(string[] strings, char delimiter = ',', bool distinct = false)
{
string outStr = "";
strings = strings.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
if(distinct)
strings = strings.Distinct().ToArray();
for (int i = 0; i < strings.Length; i++)
{
outStr += strings[i];
if (i + 1 != strings.Length)
outStr += delimiter;
}
return outStr;
}
}
}

347
Code5/OS/AiProcess.cs Normal file
View File

@@ -0,0 +1,347 @@
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Flowframes.OS;
using Flowframes.UI;
using Flowframes.Main;
using Flowframes.Data;
using Flowframes.MiscUtils;
namespace Flowframes
{
class AiProcess
{
public static bool hasShownError;
public static Process currentAiProcess;
public static Stopwatch processTime = new Stopwatch();
public static Stopwatch processTimeMulti = new Stopwatch();
public static int lastStartupTimeMs = 1000;
static string lastInPath;
public static Dictionary<string, string> filenameMap = new Dictionary<string, string>(); // TODO: Store on disk instead for crashes?
static void AiStarted (Process proc, int startupTimeMs, string inPath = "")
{
lastStartupTimeMs = startupTimeMs;
processTime.Restart();
currentAiProcess = proc;
lastInPath = string.IsNullOrWhiteSpace(inPath) ? Interpolate.current.framesFolder : inPath;
hasShownError = false;
}
static void SetProgressCheck(string interpPath, int factor)
{
int frames = IOUtils.GetAmountOfFiles(lastInPath, false, "*.png");
int target = (frames * factor) - (factor - 1);
InterpolateUtils.progressPaused = false;
InterpolateUtils.currentFactor = factor;
if (InterpolateUtils.progCheckRunning)
InterpolateUtils.targetFrames = target;
else
InterpolateUtils.GetProgressByFrameAmount(interpPath, target);
}
static async Task AiFinished (string aiName)
{
Program.mainForm.SetProgress(100);
InterpolateUtils.UpdateInterpProgress(IOUtils.GetAmountOfFiles(Interpolate.current.interpFolder, false, "*.png"), InterpolateUtils.targetFrames);
string logStr = $"Done running {aiName} - Interpolation took {FormatUtils.Time(processTime.Elapsed)}";
if (Interpolate.currentlyUsingAutoEnc && AutoEncode.HasWorkToDo())
logStr += " - Waiting for encoding to finish...";
Logger.Log(logStr);
processTime.Stop();
Stopwatch timeSinceFfmpegRan = new Stopwatch();
timeSinceFfmpegRan.Restart();
while (Interpolate.currentlyUsingAutoEnc && Program.busy)
{
if (AvProcess.lastProcess != null && !AvProcess.lastProcess.HasExited && AvProcess.lastTask == AvProcess.TaskType.Encode)
{
timeSinceFfmpegRan.Restart();
string lastLine = AvProcess.lastOutputFfmpeg.SplitIntoLines().Last();
Logger.Log(lastLine.Trim().TrimWhitespaces(), false, Logger.GetLastLine().Contains("frame"));
}
if (timeSinceFfmpegRan.ElapsedMilliseconds > 3000)
break;
// Logger.Log($"AiProcess loop - Program.busy = {Program.busy}");
await Task.Delay(500);
}
}
public static async Task RunRifeCuda(string framesPath, int interpFactor, string mdl)
{
string rifeDir = Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(Packages.rifeCuda.fileName));
string script = "inference_video.py";
if (!File.Exists(Path.Combine(rifeDir, script)))
{
Interpolate.Cancel("RIFE script not found! Make sure you didn't modify any files.");
return;
}
await RunRifeCudaProcess(framesPath, Paths.interpDir, script, interpFactor, mdl);
if (!Interpolate.canceled && Interpolate.current.alpha)
{
InterpolateUtils.progressPaused = true;
Logger.Log("Interpolating alpha channel...");
await RunRifeCudaProcess(framesPath + "-a", Paths.interpDir + "-a", script, interpFactor, mdl);
}
await AiFinished("RIFE");
if (!Interpolate.canceled && Interpolate.current.alpha)
{
Logger.Log("Processing alpha...");
string rgbInterpDir = Path.Combine(Interpolate.current.tempFolder, Paths.interpDir);
await FFmpegCommands.MergeAlphaIntoRgb(rgbInterpDir, Padding.interpFrames, rgbInterpDir + "-a", Padding.interpFrames);
}
}
public static async Task RunRifeCudaProcess (string inPath, string outDir, string script, int interpFactor, string mdl)
{
string uhdStr = await InterpolateUtils.UseUHD() ? "--UHD" : "";
string args = $" --input {inPath.Wrap()} --model {mdl} --exp {(int)Math.Log(interpFactor, 2)} {uhdStr} --imgformat {InterpolateUtils.GetOutExt()} --output {outDir}";
Process rifePy = OSUtils.NewProcess(!OSUtils.ShowHiddenCmd());
AiStarted(rifePy, 3500);
SetProgressCheck(Path.Combine(Interpolate.current.tempFolder, outDir), interpFactor);
rifePy.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {PkgUtils.GetPkgFolder(Packages.rifeCuda).Wrap()} & " +
$"set CUDA_VISIBLE_DEVICES={Config.Get("torchGpus")} & {Python.GetPyCmd()} {script} {args}";
Logger.Log($"Running RIFE {(await InterpolateUtils.UseUHD() ? "(UHD Mode)" : "")} ({script})...".TrimWhitespaces(), false);
Logger.Log("cmd.exe " + rifePy.StartInfo.Arguments, true);
if (!OSUtils.ShowHiddenCmd())
{
rifePy.OutputDataReceived += (sender, outLine) => { LogOutput("[O] " + outLine.Data, "rife-cuda-log"); };
rifePy.ErrorDataReceived += (sender, outLine) => { LogOutput("[E] " + outLine.Data, "rife-cuda-log", true); };
}
rifePy.Start();
if (!OSUtils.ShowHiddenCmd())
{
rifePy.BeginOutputReadLine();
rifePy.BeginErrorReadLine();
}
while (!rifePy.HasExited) await Task.Delay(1);
}
public static async Task RunRifeNcnn (string framesPath, string outPath, int factor, string mdl)
{
processTimeMulti.Restart();
Logger.Log($"Running RIFE{(await InterpolateUtils.UseUHD() ? " (UHD Mode)" : "")}...", false);
bool useAutoEnc = Interpolate.currentlyUsingAutoEnc;
if(factor > 2)
AutoEncode.paused = true; // Disable autoenc until the last iteration
await RunRifeNcnnProcess(framesPath, outPath, mdl);
if (factor == 4 || factor == 8) // #2
{
if (Interpolate.canceled) return;
Logger.Log("Re-Running RIFE for 4x interpolation...", false);
string run1ResultsPath = outPath + "-run1";
IOUtils.TryDeleteIfExists(run1ResultsPath);
Directory.Move(outPath, run1ResultsPath);
Directory.CreateDirectory(outPath);
if (useAutoEnc && factor == 4)
AutoEncode.paused = false;
await RunRifeNcnnProcess(run1ResultsPath, outPath, mdl);
IOUtils.TryDeleteIfExists(run1ResultsPath);
}
if (factor == 8) // #3
{
if (Interpolate.canceled) return;
Logger.Log("Re-Running RIFE for 8x interpolation...", false);
string run2ResultsPath = outPath + "-run2";
IOUtils.TryDeleteIfExists(run2ResultsPath);
Directory.Move(outPath, run2ResultsPath);
Directory.CreateDirectory(outPath);
if (useAutoEnc && factor == 8)
AutoEncode.paused = false;
await RunRifeNcnnProcess(run2ResultsPath, outPath, mdl);
IOUtils.TryDeleteIfExists(run2ResultsPath);
}
if (Interpolate.canceled) return;
if (!Interpolate.currentlyUsingAutoEnc)
IOUtils.ZeroPadDir(outPath, InterpolateUtils.GetOutExt(), Padding.interpFrames);
AiFinished("RIFE");
}
static async Task RunRifeNcnnProcess(string inPath, string outPath, string mdl)
{
Process rifeNcnn = OSUtils.NewProcess(!OSUtils.ShowHiddenCmd());
AiStarted(rifeNcnn, 1500, inPath);
SetProgressCheck(outPath, 2);
string uhdStr = await InterpolateUtils.UseUHD() ? "-u" : "";
string ttaStr = Config.GetBool("rifeNcnnUseTta", false) ? "-x" : "";
rifeNcnn.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {PkgUtils.GetPkgFolder(Packages.rifeNcnn).Wrap()} & rife-ncnn-vulkan.exe " +
$" -v -i {inPath.Wrap()} -o {outPath.Wrap()} -m {mdl.ToLower()} {ttaStr} {uhdStr} -g {Config.Get("ncnnGpus")} -f {GetNcnnPattern()} -j {GetNcnnThreads()}";
Logger.Log("cmd.exe " + rifeNcnn.StartInfo.Arguments, true);
if (!OSUtils.ShowHiddenCmd())
{
rifeNcnn.OutputDataReceived += (sender, outLine) => { LogOutput("[O] " + outLine.Data, "rife-ncnn-log"); };
rifeNcnn.ErrorDataReceived += (sender, outLine) => { LogOutput("[E] " + outLine.Data, "rife-ncnn-log", true); };
}
rifeNcnn.Start();
if (!OSUtils.ShowHiddenCmd())
{
rifeNcnn.BeginOutputReadLine();
rifeNcnn.BeginErrorReadLine();
}
while (!rifeNcnn.HasExited) await Task.Delay(1);
}
public static async Task RunDainNcnn(string framesPath, string outPath, int factor, string mdl, int tilesize)
{
string dainDir = Path.Combine(Paths.GetPkgPath(), Path.GetFileNameWithoutExtension(Packages.dainNcnn.fileName));
Process dain = OSUtils.NewProcess(!OSUtils.ShowHiddenCmd());
AiStarted(dain, 1500);
SetProgressCheck(outPath, factor);
int targetFrames = (IOUtils.GetAmountOfFiles(lastInPath, false, "*.png") * factor) - (factor - 1);
string args = $" -v -i {framesPath.Wrap()} -o {outPath.Wrap()} -n {targetFrames} -m {mdl.ToLower()}" +
$" -t {GetNcnnTilesize(tilesize)} -g {Config.Get("ncnnGpus")} -f {GetNcnnPattern()} -j 2:1:2";
dain.StartInfo.Arguments = $"{OSUtils.GetCmdArg()} cd /D {dainDir.Wrap()} & dain-ncnn-vulkan.exe {args}";
Logger.Log("Running DAIN...", false);
Logger.Log("cmd.exe " + dain.StartInfo.Arguments, true);
if (!OSUtils.ShowHiddenCmd())
{
dain.OutputDataReceived += (sender, outLine) => { LogOutput("[O] " + outLine.Data, "dain-ncnn-log"); };
dain.ErrorDataReceived += (sender, outLine) => { LogOutput("[E] " + outLine.Data, "dain-ncnn-log", true); };
}
dain.Start();
if (!OSUtils.ShowHiddenCmd())
{
dain.BeginOutputReadLine();
dain.BeginErrorReadLine();
}
while (!dain.HasExited)
await Task.Delay(1);
if (Interpolate.canceled) return;
//if (!Interpolate.currentlyUsingAutoEnc)
// IOUtils.ZeroPadDir(outPath, InterpolateUtils.GetOutExt(), Padding.interpFrames);
AiFinished("DAIN");
}
static void LogOutput (string line, string logFilename, bool err = false)
{
if (string.IsNullOrWhiteSpace(line) || line.Length < 6)
return;
Logger.LogToFile(line, false, logFilename);
if (line.Contains("ff:nocuda-cpu"))
Logger.Log("WARNING: CUDA-capable GPU device is not available, running on CPU instead!");
if (!hasShownError && err && line.ToLower().Contains("out of memory"))
{
hasShownError = true;
InterpolateUtils.ShowMessage($"Your GPU ran out of VRAM! Please try a video with a lower resolution or use the Max Video Size option in the settings.\n\n{line}", "Error");
}
if (!hasShownError && err && line.ToLower().Contains("modulenotfounderror"))
{
hasShownError = true;
InterpolateUtils.ShowMessage($"A python module is missing.\nCheck {logFilename} for details.\n\n{line}", "Error");
if(!Python.HasEmbeddedPyFolder())
Process.Start("https://github.com/n00mkrad/flowframes/blob/main/PythonDependencies.md");
}
if (!hasShownError && line.ToLower().Contains("no longer supports this gpu"))
{
hasShownError = true;
InterpolateUtils.ShowMessage($"Your GPU seems to be outdated and is not supported!\n\n{line}", "Error");
}
if (!hasShownError && err && (line.Contains("RuntimeError") || line.Contains("ImportError") || line.Contains("OSError")))
{
hasShownError = true;
InterpolateUtils.ShowMessage($"A python error occured during interpolation!\nCheck {logFilename} for details.\n\n{line}", "Error");
}
if (!hasShownError && err && line.Contains("vk") && line.Contains(" failed"))
{
hasShownError = true;
string dain = (Interpolate.current.ai.aiName == Networks.dainNcnn.aiName) ? "\n\nTry reducing the tile size in the AI settings." : "";
InterpolateUtils.ShowMessage($"A Vulkan error occured during interpolation!\n\n{line}{dain}", "Error");
}
if (!hasShownError && err && line.Contains("vkAllocateMemory failed"))
{
hasShownError = true;
bool usingDain = (Interpolate.current.ai.aiName == Networks.dainNcnn.aiName);
string msg = usingDain ? "\n\nTry reducing the tile size in the AI settings." : "Try a lower resolution (Settings -> Max Video Size).";
InterpolateUtils.ShowMessage($"Vulkan ran out of memory!\n\n{line}{msg}", "Error");
}
if (!hasShownError && err && line.Contains("invalid gpu device"))
{
hasShownError = true;
InterpolateUtils.ShowMessage($"A Vulkan error occured during interpolation!\n\n{line}\n\nAre your GPU IDs set correctly?", "Error");
}
if (hasShownError)
Interpolate.Cancel();
}
static string GetNcnnPattern ()
{
return $"%0{Padding.interpFrames}d.{InterpolateUtils.GetOutExt()}";
}
static string GetNcnnTilesize(int tilesize)
{
int gpusAmount = Config.Get("ncnnGpus").Split(',').Length;
string tilesizeStr = $"{tilesize}";
for (int i = 1; i < gpusAmount; i++)
tilesizeStr += $",{tilesize}";
return tilesizeStr;
}
static string GetNcnnThreads ()
{
int gpusAmount = Config.Get("ncnnGpus").Split(',').Length;
int procThreads = Config.GetInt("ncnnThreads");
string progThreadsStr = $"{procThreads}";
for (int i = 1; i < gpusAmount; i++)
progThreadsStr += $",{procThreads}";
return $"4:{progThreadsStr}:4"; ;
}
}
}

76
Code5/OS/NvApi.cs Normal file
View File

@@ -0,0 +1,76 @@
using NvAPIWrapper;
using NvAPIWrapper.GPU;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.OS
{
class NvApi
{
static PhysicalGPU gpu;
public static async void Init()
{
try
{
NVIDIA.Initialize();
PhysicalGPU[] gpus = PhysicalGPU.GetPhysicalGPUs();
if (gpus.Length == 0)
return;
gpu = gpus[0];
Logger.Log($"Initialized NvApi. GPU: {gpu.FullName}");
}
catch (Exception e)
{
Logger.Log($"Failed to initialize NvApi: {e.Message}\nIgnore this if you don't have an Nvidia GPU.");
}
}
public static float GetVramGb ()
{
try
{
return (gpu.MemoryInformation.AvailableDedicatedVideoMemoryInkB / 1000f / 1024f);
}
catch (Exception e)
{
return 0f;
}
}
public static float GetFreeVramGb()
{
try
{
return (gpu.MemoryInformation.CurrentAvailableDedicatedVideoMemoryInkB / 1000f / 1024f);
}
catch (Exception e)
{
return 0f;
}
}
public static string GetGpuName()
{
try
{
NVIDIA.Initialize();
PhysicalGPU[] gpus = PhysicalGPU.GetPhysicalGPUs();
if (gpus.Length == 0)
return "";
return gpus[0].FullName;
}
catch
{
return "";
}
}
}
}

139
Code5/OS/OSUtils.cs Normal file
View File

@@ -0,0 +1,139 @@
using System.Collections.Generic;
using System.Text;
using System.Security.Principal;
using System;
using System.Diagnostics;
using System.Management;
using Flowframes.IO;
using DiskDetector;
using DiskDetector.Models;
using Microsoft.VisualBasic.Devices;
namespace Flowframes.OS
{
class OSUtils
{
public static bool IsUserAdministrator()
{
//bool value to hold our return value
bool isAdmin;
WindowsIdentity user = null;
try
{
//get the currently logged in user
user = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(user);
isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator);
}
catch (UnauthorizedAccessException ex)
{
isAdmin = false;
}
catch (Exception ex)
{
isAdmin = false;
}
finally
{
if (user != null)
user.Dispose();
}
return isAdmin;
}
public static Process SetStartInfo(Process proc, bool hidden, string filename = "cmd.exe")
{
proc.StartInfo.UseShellExecute = !hidden;
proc.StartInfo.RedirectStandardOutput = hidden;
proc.StartInfo.RedirectStandardError = hidden;
proc.StartInfo.CreateNoWindow = hidden;
proc.StartInfo.FileName = filename;
return proc;
}
public static Process NewProcess(bool hidden, string filename = "cmd.exe")
{
Process proc = new Process();
return SetStartInfo(proc, hidden, filename);
}
public static void KillProcessTree(int pid)
{
ManagementObjectSearcher processSearcher = new ManagementObjectSearcher
("Select * From Win32_Process Where ParentProcessID=" + pid);
ManagementObjectCollection processCollection = processSearcher.Get();
try
{
Process proc = Process.GetProcessById(pid);
if (!proc.HasExited) proc.Kill();
}
catch (ArgumentException)
{
// Process already exited.
}
if (processCollection != null)
{
foreach (ManagementObject mo in processCollection)
{
KillProcessTree(Convert.ToInt32(mo["ProcessID"])); //kill child processes(also kills childrens of childrens etc.)
}
}
}
public static string GetCmdArg()
{
bool stayOpen = Config.GetInt("cmdDebugMode") == 2;
if (stayOpen)
return "/K";
else
return "/C";
}
public static bool ShowHiddenCmd()
{
return Config.GetInt("cmdDebugMode") > 0;
}
public static bool DriveIsSSD(string path)
{
try
{
var detectedDrives = Detector.DetectFixedDrives(QueryType.SeekPenalty);
if (detectedDrives.Count != 0)
{
char pathDriveLetter = (path[0].ToString().ToUpper())[0];
foreach (var detectedDrive in detectedDrives)
{
if (detectedDrive.DriveLetter == pathDriveLetter && detectedDrive.HardwareType.ToString().ToLower().Trim() == "ssd")
return true;
}
}
}
catch (Exception e)
{
Logger.Log("Failed to detect drive type: " + e.Message);
return true; // Default to SSD on fail
}
return false;
}
public static bool HasNonAsciiChars(string str)
{
return (Encoding.UTF8.GetByteCount(str) != str.Length);
}
public static int GetFreeRamMb ()
{
try
{
return (int)(new ComputerInfo().AvailablePhysicalMemory / 1048576);
}
catch
{
return 1000;
}
}
}
}

182
Code5/OS/Python.cs Normal file
View File

@@ -0,0 +1,182 @@
using Flowframes.IO;
using Flowframes.MiscUtils;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.OS
{
class Python
{
static bool hasCheckedSysPy = false;
static bool sysPyInstalled = false;
public static string compactOutput;
public static async Task CheckCompression ()
{
if(HasEmbeddedPyFolder() && (Config.Get("compressedPyVersion") != Updater.GetInstalledVer().ToString()))
{
Program.mainForm.SetWorking(true, false);
Stopwatch sw = new Stopwatch();
sw.Restart();
try
{
bool shownPatienceMsg = false;
Logger.Log("Compressing python runtime. This only needs to be done once.");
compactOutput = "";
Process compact = OSUtils.NewProcess(true);
compact.StartInfo.Arguments = $"/C compact /C /S:{GetPyFolder().Wrap()} /EXE:LZX";
compact.OutputDataReceived += new DataReceivedEventHandler(CompactOutputHandler);
compact.ErrorDataReceived += new DataReceivedEventHandler(CompactOutputHandler);
compact.Start();
compact.BeginOutputReadLine();
compact.BeginErrorReadLine();
while (!compact.HasExited)
{
await Task.Delay(500);
if(sw.ElapsedMilliseconds > 10000)
{
Logger.Log($"This can take up to a few minutes... (Elapsed: {FormatUtils.Time(sw.Elapsed)})", false, shownPatienceMsg);
shownPatienceMsg = true;
await Task.Delay(500);
}
}
Config.Set("compressedPyVersion", Updater.GetInstalledVer().ToString());
Logger.Log("Done compressing python runtime.");
Logger.WriteToFile(compactOutput, true, "compact");
}
catch { }
Program.mainForm.SetWorking(false);
}
}
static void CompactOutputHandler (object sendingProcess, DataReceivedEventArgs outLine)
{
if (outLine == null || outLine.Data == null)
return;
string line = outLine.Data;
compactOutput = compactOutput + line + "\n";
}
public static string GetPyCmd ()
{
if (HasEmbeddedPyFolder())
{
Logger.Log("Using embedded Python runtime.");
return Path.Combine(GetPyFolder(), "python.exe").Wrap();
}
else
{
if (IsSysPyInstalled())
{
return "python";
}
else
{
MessageBox.Show("System python installation not found!\nPlease install Python or download the package from the package installer.");
Interpolate.Cancel("Neither the Flowframes Python Runtime nor System Python installation could be found!");
}
}
return "";
}
public static bool HasEmbeddedPyFolder ()
{
return (Directory.Exists(GetPyFolder()) && IOUtils.GetDirSize(GetPyFolder(), false) > 1024 * 1024 * 5);
}
public static string GetPyFolder ()
{
if (Directory.Exists(Path.Combine(Paths.GetPkgPath(), "py-amp")))
return Path.Combine(Paths.GetPkgPath(), "py-amp");
if (Directory.Exists(Path.Combine(Paths.GetPkgPath(), "py-tu")))
return Path.Combine(Paths.GetPkgPath(), "py-tu");
return "";
}
public static bool IsPytorchReady ()
{
string torchVer = GetPytorchVer();
if (!string.IsNullOrWhiteSpace(torchVer) && torchVer.Length <= 35)
return true;
else
return false;
}
static string GetPytorchVer()
{
try
{
Process py = OSUtils.NewProcess(true);
py.StartInfo.Arguments = "\"/C\" " + GetPyCmd() + " -c \"import torch; print(torch.__version__)\"";
Logger.Log("[DepCheck] CMD: " + py.StartInfo.Arguments);
py.Start();
py.WaitForExit();
string output = py.StandardOutput.ReadToEnd();
string err = py.StandardError.ReadToEnd();
if (!string.IsNullOrWhiteSpace(err)) output += "\n" + err;
Logger.Log("[DepCheck] Pytorch Check Output: " + output.Trim());
return output;
}
catch
{
return "";
}
}
public static bool IsSysPyInstalled ()
{
if (hasCheckedSysPy)
return sysPyInstalled;
bool isInstalled = false;
Logger.Log("Checking if system Python is available...", true);
string sysPyVer = GetSysPyVersion();
if (!string.IsNullOrWhiteSpace(sysPyVer) && !sysPyVer.ToLower().Contains("not found") && sysPyVer.Length <= 35)
{
isInstalled = true;
Logger.Log("Using Python installation: " + sysPyVer, true);
}
hasCheckedSysPy = true;
sysPyInstalled = isInstalled;
return sysPyInstalled;
}
static string GetSysPyVersion()
{
string pythonOut = GetSysPythonOutput();
Logger.Log("[DepCheck] System Python Check Output: " + pythonOut.Trim(), true);
try
{
string ver = pythonOut.Split('(')[0].Trim();
Logger.Log("[DepCheck] Sys Python Ver: " + ver, true);
return ver;
}
catch
{
return "";
}
}
static string GetSysPythonOutput()
{
Process py = OSUtils.NewProcess(true);
py.StartInfo.Arguments = "/C python -V";
Logger.Log("[DepCheck] CMD: " + py.StartInfo.Arguments, true);
py.Start();
py.WaitForExit();
string output = py.StandardOutput.ReadToEnd();
string err = py.StandardError.ReadToEnd();
return output + "\n" + err;
}
}
}

149
Code5/OS/Updater.cs Normal file
View File

@@ -0,0 +1,149 @@
using Flowframes.Data;
using Flowframes.Forms;
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.OS
{
class Updater
{
public static string latestVerUrl = "https://dl.nmkd.de/flowframes/exe/ver.ini";
public static SemVer GetInstalledVer()
{
try
{
string verStr = IOUtils.ReadLines(Paths.GetVerPath())[0];
return new SemVer(verStr);
}
catch (Exception e)
{
Logger.Log("Error getting installed version: " + e.Message);
return new SemVer(0, 0, 0);
}
}
public static bool IsVersionNewer (SemVer currentVer, SemVer newVer)
{
if (newVer.major > currentVer.major)
{
return true;
}
else
{
if(newVer.minor > currentVer.minor)
{
return true;
}
else
{
if (newVer.patch > currentVer.patch)
{
return true;
}
else
{
return false;
}
}
}
}
public static bool VersionMatches (SemVer v1, SemVer v2)
{
return v1.major == v2.major && v1.minor == v2.minor && v1.patch == v2.patch;
}
public static SemVer GetLatestVer (bool patreon)
{
var client = new WebClient();
int line = patreon ? 0 : 2;
return new SemVer(client.DownloadString(latestVerUrl).SplitIntoLines()[line]);
}
public static string GetLatestVerLink(bool patreon)
{
int line = patreon ? 1 : 3;
var client = new WebClient();
try
{
return client.DownloadString(latestVerUrl).SplitIntoLines()[line].Trim();
}
catch
{
Logger.Log("Failed to get latest version link from ver.ini!", true);
return "";
}
}
public static async Task UpdateTo (int version, UpdaterForm form = null)
{
Logger.Log("Updating to " + version, true);
string savePath = Path.Combine(IOUtils.GetExeDir(), $"FlowframesV{version}");
try
{
var client = new WebClient();
client.DownloadProgressChanged += async (sender, args) =>
{
if (form != null && (args.ProgressPercentage % 5 == 0))
{
Logger.Log("Downloading update... " + args.ProgressPercentage, true);
form.SetProgLabel(args.ProgressPercentage, $"Downloading latest version... {args.ProgressPercentage}%");
await Task.Delay(20);
}
};
client.DownloadFileCompleted += (sender, args) =>
{
form.SetProgLabel(100f, $"Downloading latest version... 100%");
};
await client.DownloadFileTaskAsync(new Uri($"https://dl.nmkd.de/flowframes/exe/{version}/Flowframes.exe"), savePath);
}
catch (Exception e)
{
MessageBox.Show("Error: Failed to download update.\n\n" + e.Message, "Error");
Logger.Log("Updater Error during download: " + e.Message, true);
return;
}
try
{
Logger.Log("Installing v" + version, true);
string runningExePath = IOUtils.GetExe();
string oldExePath = runningExePath + ".old";
IOUtils.TryDeleteIfExists(oldExePath);
File.Move(runningExePath, oldExePath);
File.Move(savePath, runningExePath);
}
catch (Exception e)
{
MessageBox.Show("Error: Failed to install update.\n\n" + e.Message, "Error");
Logger.Log("Updater Error during install: " + e.Message, true);
return;
}
form.SetProgLabel(101f, $"Update downloaded.");
await Task.Delay(20);
MessageBox.Show("Update was installed!\nFlowframes will now close. Restart it to use the new version.", "Message");
Application.Exit();
}
public static async Task AsyncUpdateCheck ()
{
SemVer installed = GetInstalledVer();
SemVer latestPat = GetLatestVer(true);
SemVer latestFree = GetLatestVer(false);
Logger.Log($"You are running Flowframes {installed}. The latest Patreon version is {latestPat}, the latest free version is {latestFree}.");
// if (IsVersionNewer(installed, latest))
// Logger.Log($"An update for Flowframes ({latest}) is available! Download it from the Updater.");
// else
// Logger.Log($"Flowframes is up to date ({installed}).");
}
}
}

47
Code5/Program.cs Normal file
View File

@@ -0,0 +1,47 @@
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.OS;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes
{
static class Program
{
public static Form1 mainForm;
public static bool busy = false;
public static string lastInputPath;
public static bool lastInputPathIsSsd;
public static Queue<InterpSettings> batchQueue = new Queue<InterpSettings>();
[STAThread]
static void Main()
{
Paths.Init();
Config.Init();
if (Config.GetBool("delLogsOnStartup"))
IOUtils.DeleteContentsOfDir(Paths.GetLogPath()); // Clear out older logs not from this session
string oldExePath = IOUtils.GetExe() + ".old";
IOUtils.TryDeleteIfExists(oldExePath);
Networks.Init();
NvApi.Init();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
mainForm = new Form1();
Application.Run(mainForm);
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Flowframes")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Flowframes")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("389e42dd-a163-49d7-9e0a-ae41090a07b3")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

243
Code5/Properties/Resources.Designer.cs generated Normal file
View File

@@ -0,0 +1,243 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Flowframes.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Flowframes.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap baseline_create_white_18dp {
get {
object obj = ResourceManager.GetObject("baseline_create_white_18dp", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap baseline_create_white_18dp_semiTransparent {
get {
object obj = ResourceManager.GetObject("baseline_create_white_18dp-semiTransparent", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap baseline_fact_check_white_48dp {
get {
object obj = ResourceManager.GetObject("baseline_fact_check_white_48dp", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap baseline_image_white_48dp {
get {
object obj = ResourceManager.GetObject("baseline_image_white_48dp", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap baseline_image_white_48dp_4x {
get {
object obj = ResourceManager.GetObject("baseline_image_white_48dp-4x", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap baseline_image_white_48dp_4x_25pcAlpha {
get {
object obj = ResourceManager.GetObject("baseline_image_white_48dp-4x-25pcAlpha", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap baseline_queue_white_48dp {
get {
object obj = ResourceManager.GetObject("baseline_queue_white_48dp", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap baseline_settings_white_48dp {
get {
object obj = ResourceManager.GetObject("baseline_settings_white_48dp", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap baseline_system_update_alt_white_48dp {
get {
object obj = ResourceManager.GetObject("baseline_system_update_alt_white_48dp", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap discordIco {
get {
object obj = ResourceManager.GetObject("discordIco", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap discordIcoColored {
get {
object obj = ResourceManager.GetObject("discordIcoColored", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap flowframesIcoNew_512px {
get {
object obj = ResourceManager.GetObject("flowframesIcoNew_512px", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap patreon256px {
get {
object obj = ResourceManager.GetObject("patreon256px", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap patreon256pxColored {
get {
object obj = ResourceManager.GetObject("patreon256pxColored", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap paypal256px {
get {
object obj = ResourceManager.GetObject("paypal256px", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap questmark_72px_bordeer {
get {
object obj = ResourceManager.GetObject("questmark-72px-bordeer", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap separatorTest1 {
get {
object obj = ResourceManager.GetObject("separatorTest1", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] x64_7za {
get {
object obj = ResourceManager.GetObject("x64_7za", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@@ -0,0 +1,175 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="baseline_image_white_48dp-4x" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\baseline_image_white_48dp-4x.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="baseline_queue_white_48dp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\baseline_queue_white_48dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="x64_7za" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\7za.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="baseline_fact_check_white_48dp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\baseline_fact_check_white_48dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="paypal256px" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\paypal256px.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="baseline_system_update_alt_white_48dp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\baseline_system_update_alt_white_48dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="patreon256px" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\patreon256px.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="questmark-72px-bordeer" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\questmark-72px-bordeer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="baseline_create_white_18dp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\baseline_create_white_18dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="discordIco" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\discordIco.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="patreon256pxColored" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\patreon256pxColored.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="discordIcoColored" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\discordIcoColored.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="separatorTest1" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\separatorTest1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="baseline_image_white_48dp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\baseline_image_white_48dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="baseline_create_white_18dp-semiTransparent" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\baseline_create_white_18dp-semiTransparent.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="baseline_settings_white_48dp" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\baseline_settings_white_48dp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="baseline_image_white_48dp-4x-25pcAlpha" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\baseline_image_white_48dp-4x-25pcAlpha.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="flowframesIcoNew_512px" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\flowframesIcoNew-512px.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

26
Code5/Properties/Settings.Designer.cs generated Normal file
View File

@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Flowframes.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

BIN
Code5/Resources/7za.exe Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

70
Code5/UI/GetWebInfo.cs Normal file
View File

@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.UI
{
class GetWebInfo
{
public static async Task LoadNews (Label newsLabel)
{
string url = $"http://dl.nmkd.de/flowframes/changelog.txt";
var client = new WebClient();
var str = await client.DownloadStringTaskAsync(new Uri(url));
newsLabel.Text = str;
}
public static async Task LoadPatronList(Label patronsLabel)
{
string url = $"http://dl.nmkd.de/flowframes/patreon.txt";
var client = new WebClient();
var str = await client.DownloadStringTaskAsync(new Uri(url));
patronsLabel.Text = str;
}
public static async Task LoadPatronListCsv(Label patronsLabel)
{
string url = $"http://dl.nmkd.de/flowframes/patrons.csv";
var client = new WebClient();
var csvData = await client.DownloadStringTaskAsync(new Uri(url));
patronsLabel.Text = ParsePatreonCsv(csvData);
}
public static string ParsePatreonCsv(string csvData)
{
List<string> goldPatrons = new List<string>();
List<string> silverPatrons = new List<string>();
string str = "Gold:\n";
string[] lines = csvData.SplitIntoLines();
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
string[] values = line.Split(',');
if (i == 0 || line.Length < 10 || values.Length < 5) continue;
string name = values[0];
float amount = float.Parse(values[7], System.Globalization.CultureInfo.InvariantCulture);
if(amount >= 4.5f)
{
if (amount >= 11f)
goldPatrons.Add(name);
else
silverPatrons.Add(name);
}
}
foreach (string pat in goldPatrons)
str += pat + "\n";
str += "\nSilver:\n";
foreach (string pat in silverPatrons)
str += pat + "\n";
return str;
}
}
}

125
Code5/UI/MainUiFunctions.cs Normal file
View File

@@ -0,0 +1,125 @@
using Flowframes.IO;
using Flowframes.Magick;
using Flowframes.Main;
using Flowframes.OS;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.UI
{
class MainUiFunctions
{
public static async Task InitInput (TextBox outputTbox, TextBox inputTbox, TextBox fpsInTbox)
{
Program.mainForm.SetTab("interpolate");
if (Config.GetBool("clearLogOnInput"))
Logger.ClearLogBox();
outputTbox.Text = inputTbox.Text.Trim().GetParentDir();
string path = inputTbox.Text.Trim();
Program.lastInputPath = path;
Program.lastInputPathIsSsd = OSUtils.DriveIsSSD(path);
if (!Program.lastInputPathIsSsd)
Logger.Log("Your file seems to be on an HDD or USB device. It is recommended to interpolate videos on an SSD drive for best performance.");
Logger.Log("Loading metadata...");
int frameCount = await InterpolateUtils.GetInputFrameCountAsync(path);
string fpsStr = "Not Found";
float fps = IOUtils.GetFpsFolderOrVideo(path);
if (fps > 0)
{
fpsStr = fps.ToString();
fpsInTbox.Text = fpsStr;
}
if (IOUtils.IsPathDirectory(path))
Logger.Log($"Video FPS (Loaded from fps.ini): {fpsStr} - Total Number Of Frames: {frameCount}", false, true);
else
Logger.Log($"Video FPS: {fpsStr} - Total Number Of Frames: {frameCount}", false, true);
Program.mainForm.currInFps = fps;
Program.mainForm.currInFrames = frameCount;
Program.mainForm.UpdateInputInfo();
CheckExistingFolder(path, outputTbox.Text.Trim());
await Task.Delay(10);
await PrintResolution(path);
Dedupe.ClearCache();
await Task.Delay(10);
InterpolateUtils.SetPreviewImg(await GetThumbnail(path));
}
static void CheckExistingFolder (string inpath, string outpath)
{
if (Config.GetInt("processingMode") == 0) return;
string tmpFolder = InterpolateUtils.GetTempFolderLoc(inpath, outpath);
if (Directory.Exists(tmpFolder))
{
int scnFrmAmount = IOUtils.GetAmountOfFiles(Path.Combine(tmpFolder, Paths.scenesDir), false, "*.png");
string scnFrames = scnFrmAmount > 0 ? $"{scnFrmAmount} scene frames" : "no scene frames";
int srcFrmAmount = IOUtils.GetAmountOfFiles(Path.Combine(tmpFolder, Paths.framesDir), false, "*.png");
string srcFrames = srcFrmAmount > 1 ? $"{srcFrmAmount} source frames" : "no source frames";
int interpFrmAmount = IOUtils.GetAmountOfFiles(Path.Combine(tmpFolder, Paths.interpDir), false);
string interpFrames = interpFrmAmount > 2 ? $"{interpFrmAmount} interpolated frames" : "no interpolated frames";
string msg = $"A temporary folder for this video already exists. It contains {scnFrames}, {srcFrames}, {interpFrames}.";
DialogResult dialogResult = MessageBox.Show($"{msg}\n\nClick \"Yes\" to use the existing files or \"No\" to delete them.", "Use files from existing temp folder?", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
return;
}
else if (dialogResult == DialogResult.No)
{
IOUtils.TryDeleteIfExists(tmpFolder);
Logger.Log("Deleted old temp folder.");
}
}
}
static async Task PrintResolution (string path)
{
Size res = new Size();
if(path == Interpolate.current.inPath)
res = await Interpolate.current.GetInputRes();
else
res = await IOUtils.GetVideoOrFramesRes(path);
if (res.Width > 1 && res.Height > 1)
Logger.Log($"Input Resolution: {res.Width}x{res.Height}");
Program.mainForm.currInRes = res;
Program.mainForm.UpdateInputInfo();
}
public static async Task<Image> GetThumbnail (string path)
{
string imgOnDisk = Path.Combine(Paths.GetDataPath(), "thumb-temp.jpg");
try
{
if (!IOUtils.IsPathDirectory(path)) // If path is video - Extract first frame
{
await FFmpegCommands.ExtractSingleFrame(path, imgOnDisk, 1);
return IOUtils.GetImage(imgOnDisk);
}
else // Path is frame folder - Get first frame
{
return IOUtils.GetImage(IOUtils.GetFilesSorted(path)[0]);
}
}
catch (Exception e)
{
Logger.Log("GetThumbnail Error: " + e.Message, true);
return null;
}
}
}
}

63
Code5/UI/UIUtils.cs Normal file
View File

@@ -0,0 +1,63 @@
using Flowframes.Data;
using Flowframes.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.UI
{
class UIUtils
{
public static void InitCombox(ComboBox box, int index)
{
if (box.Items.Count >= 1)
{
box.SelectedIndex = index;
box.Text = box.Items[index].ToString();
}
}
public static bool AssignComboxIndexFromText (ComboBox box, string text) // Set index to corresponding text
{
int index = box.Items.IndexOf(text);
if (index == -1) // custom value, index not found
return false;
box.SelectedIndex = index;
return true;
}
public static ComboBox FillAiModelsCombox (ComboBox combox, AI ai)
{
string pkgPath = PkgUtils.GetPkgFolder(ai.pkg);
string modelsFile = Path.Combine(pkgPath, "models.txt");
string[] modelsWithDec = IOUtils.ReadLines(modelsFile);
combox.Items.Clear();
for (int i = 0; i < modelsWithDec.Length; i++)
{
string model = modelsWithDec[i];
if (string.IsNullOrWhiteSpace(model))
continue;
combox.Items.Add(model);
if (model.Contains("Recommended") || model.Contains("Default"))
combox.SelectedIndex = i;
}
if(combox.SelectedIndex < 0)
combox.SelectedIndex = 0;
return combox;
}
}
}

112
Code5/UI/UtilsTab.cs Normal file
View File

@@ -0,0 +1,112 @@
using Flowframes.IO;
using Flowframes.Magick;
using Flowframes.Main;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Flowframes.UI
{
class UtilsTab
{
public static async Task ExtractVideo(string videoPath, bool withAudio)
{
string outPath = Path.ChangeExtension(videoPath, null) + "-extracted";
Program.mainForm.SetWorking(true);
await FFmpegCommands.VideoToFrames(videoPath, Path.Combine(outPath, Paths.framesDir), false, Interpolate.current.inFps, false, false, false);
File.WriteAllText(Path.Combine(outPath, "fps.ini"), Interpolate.current.inFps.ToString());
if (withAudio)
await FFmpegCommands.ExtractAudio(videoPath, Path.Combine(outPath, "audio"));
Program.mainForm.SetWorking(false);
Logger.Log("Done.");
}
public static async Task LoopVideo (string inputFile, ComboBox loopTimes)
{
if (!InputIsValid(inputFile))
return;
int times = loopTimes.GetInt();
Logger.Log("Lopping video " + times + "x...", true);
await FFmpegCommands.LoopVideo(inputFile, times, false);
Logger.Log("Done", true);
}
public static async Task ChangeSpeed(string inputFile, ComboBox speed)
{
if (!InputIsValid(inputFile))
return;
float speedFloat = speed.GetFloat();
Logger.Log("Creating video with " + speed + "% speed...", true);
await FFmpegCommands.ChangeSpeed(inputFile, speedFloat, false);
Logger.Log("Done", true);
}
public static async Task Convert(string inputFile, ComboBox crfBox)
{
if (!InputIsValid(inputFile))
return;
int crf = crfBox.GetInt();
Logger.Log("Creating MP4 with CRF " + crf + "...", true);
if(Path.GetExtension(inputFile).ToUpper() != ".MP4")
await FFmpegCommands.Encode(inputFile, "libx264", "aac", crf, 128);
else
await FFmpegCommands.Encode(inputFile, "libx264", "copy", crf); // Copy audio if input is MP4
Logger.Log("Done", true);
}
static bool InputIsValid (string inPath)
{
bool isFile = !IOUtils.IsPathDirectory(inPath);
if ((isFile && !IOUtils.IsFileValid(inPath)) || (!isFile && !IOUtils.IsDirValid(inPath)))
{
MessageBox.Show("Input path is not valid!");
return false;
}
return true;
}
public static async void Dedupe (string inPath, bool testRun)
{
bool isFile = !IOUtils.IsPathDirectory(inPath);
if ((isFile && !IOUtils.IsFileValid(inPath)) || (!isFile && !IOUtils.IsDirValid(inPath)))
{
MessageBox.Show("Input path is not valid!");
return;
}
string framesPath;
if (isFile)
{
Logger.Log("Input is a file, not directory");
if (!InterpolateUtils.IsVideoValid(inPath))
{
MessageBox.Show("Input file is not valid!", "Error");
return;
}
Program.mainForm.SetWorking(true);
await Task.Delay(10);
framesPath = Path.ChangeExtension(inPath, null) + "-frames";
Directory.CreateDirectory(framesPath);
await Interpolate.ExtractFrames(inPath, framesPath, false);
}
else
{
framesPath = inPath;
}
Program.mainForm.SetWorking(true);
Logger.Log("Running frame de-duplication", true);
await Task.Delay(10);
await Magick.Dedupe.Run(framesPath, testRun);
IOUtils.TryDeleteIfExists(framesPath);
Program.mainForm.SetProgress(0);
Program.mainForm.SetWorking(false);
}
}
}

BIN
Code5/flowframesIcoNew.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB