mirror of
https://github.com/n00mkrad/flowframes.git
synced 2026-09-01 19:51:28 +02:00
Support deduplication without frame extraction with VS (WIP)
This commit is contained in:
@@ -848,6 +848,9 @@ namespace Flowframes.IO
|
||||
|
||||
public static FileInfo[] GetFileInfosSorted(string path, bool recursive = false, string pattern = "*")
|
||||
{
|
||||
if(!Directory.Exists(path))
|
||||
return new FileInfo[0];
|
||||
|
||||
SearchOption opt = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
||||
DirectoryInfo dir = new DirectoryInfo(path);
|
||||
return dir.GetFiles(pattern, opt).OrderBy(x => x.Name).ToArray();
|
||||
|
||||
@@ -8,6 +8,8 @@ using System.Threading;
|
||||
using Flowframes.IO;
|
||||
using ImageMagick;
|
||||
using Newtonsoft.Json;
|
||||
using Flowframes.Os;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Flowframes.Magick
|
||||
{
|
||||
@@ -224,14 +226,14 @@ namespace Flowframes.Magick
|
||||
return GetDifference(GetImage(img1Path), GetImage(img2Path));
|
||||
}
|
||||
|
||||
public static async Task CreateDupesFile(string framesPath, int lastFrameNum, string ext)
|
||||
public static async Task CreateDupesFile(string framesPath, string ext)
|
||||
{
|
||||
bool debug = Config.GetBool("dupeScanDebug", false);
|
||||
|
||||
FileInfo[] frameFiles = IoUtils.GetFileInfosSorted(framesPath, false, "*" + ext);
|
||||
|
||||
if (debug)
|
||||
Logger.Log($"Running CreateDupesFile for '{framesPath}' ({frameFiles.Length} files), lastFrameNum = {lastFrameNum}, ext = {ext}.", true, false, "dupes");
|
||||
Logger.Log($"Running CreateDupesFile for '{framesPath}' ({frameFiles.Length} files), ext = {ext}.", true, false, "dupes");
|
||||
|
||||
Dictionary<string, List<string>> frames = new Dictionary<string, List<string>>();
|
||||
|
||||
@@ -257,7 +259,55 @@ namespace Flowframes.Magick
|
||||
}
|
||||
}
|
||||
|
||||
File.WriteAllText(Path.Combine(framesPath.GetParentDir(), "dupes.json"), JsonConvert.SerializeObject(frames, Formatting.Indented));
|
||||
File.WriteAllText(Path.Combine(framesPath.GetParentDir(), "dupes.json"), frames.ToJson(true));
|
||||
}
|
||||
|
||||
public static async Task CreateFramesFileVideo(string videoPath, bool loop)
|
||||
{
|
||||
if (!Directory.Exists(Interpolate.currentSettings.tempFolder))
|
||||
Directory.CreateDirectory(Interpolate.currentSettings.tempFolder);
|
||||
|
||||
Process ffmpeg = OsUtils.NewProcess(true);
|
||||
string baseCmd = $"/C cd /D {Path.Combine(IO.Paths.GetPkgPath(), IO.Paths.audioVideoDir).Wrap()}";
|
||||
string mpDec = FfmpegCommands.GetMpdecimate((int)FfmpegCommands.MpDecSensitivity.Normal, false);
|
||||
ffmpeg.StartInfo.Arguments = $"{baseCmd} & ffmpeg -loglevel debug -y -i {videoPath.Wrap()} -fps_mode vfr -vf {mpDec} -f null NUL 2>&1 | findstr keep_count:";
|
||||
List<string> ffmpegOutputLines = (await Task.Run(() => OsUtils.GetProcStdOut(ffmpeg, true))).SplitIntoLines().Where(l => l.IsNotEmpty()).ToList();
|
||||
|
||||
var frames = new Dictionary<int, List<int>>();
|
||||
var frameNums = new List<int>();
|
||||
int lastKeepFrameNum = 0;
|
||||
|
||||
for (int frameIdx = 0; frameIdx < ffmpegOutputLines.Count; frameIdx++)
|
||||
{
|
||||
string line = ffmpegOutputLines[frameIdx];
|
||||
bool drop = frameIdx != 0 && line.Contains(" drop ") && !line.Contains(" keep ");
|
||||
// Console.WriteLine($"[Frame {frameIdx.ToString().PadLeft(6, '0')}] {(drop ? "DROP" : "KEEP")}");
|
||||
// frameNums.Add(lastKeepFrameNum);
|
||||
|
||||
if (!drop)
|
||||
{
|
||||
if (!frames.ContainsKey(frameIdx) || frames[frameIdx] == null)
|
||||
{
|
||||
frames[frameIdx] = new List<int>();
|
||||
}
|
||||
|
||||
lastKeepFrameNum = frameIdx;
|
||||
}
|
||||
else
|
||||
{
|
||||
frames[lastKeepFrameNum].Add(frameIdx);
|
||||
}
|
||||
}
|
||||
|
||||
var inputFrames = new List<int>(frames.Keys);
|
||||
|
||||
if (loop)
|
||||
{
|
||||
inputFrames.Add(inputFrames.First());
|
||||
}
|
||||
|
||||
File.WriteAllText(Path.Combine(Interpolate.currentSettings.tempFolder, "input.json"), inputFrames.ToJson(true));
|
||||
File.WriteAllText(Path.Combine(Interpolate.currentSettings.tempFolder, "dupes.test.json"), frames.ToJson(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Flowframes.Data;
|
||||
using Flowframes.IO;
|
||||
using Flowframes.MiscUtils;
|
||||
using Flowframes.Os;
|
||||
using Flowframes.Properties;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -13,25 +15,30 @@ namespace Flowframes.Main
|
||||
{
|
||||
class FrameOrder
|
||||
{
|
||||
static Stopwatch benchmark = new Stopwatch();
|
||||
static FileInfo[] frameFiles;
|
||||
static FileInfo[] frameFilesWithoutLast;
|
||||
static List<string> sceneFrames = new List<string>();
|
||||
static Dictionary<int, string> frameFileContents = new Dictionary<int, string>();
|
||||
static List<string> inputFilenames = new List<string>();
|
||||
static int lastOutFileCount;
|
||||
private static Stopwatch benchmark = new Stopwatch();
|
||||
private static FileInfo[] frameFiles;
|
||||
private static FileInfo[] frameFilesWithoutLast;
|
||||
private static List<string> sceneFrames = new List<string>();
|
||||
private static Dictionary<int, string> frameFileContents = new Dictionary<int, string>();
|
||||
private static List<string> inputFilenames = new List<string>();
|
||||
private static int lastOutFileCount;
|
||||
|
||||
public static async Task CreateFrameOrderFile(string framesPath, bool loopEnabled, float times)
|
||||
public static async Task CreateFrameOrderFile(string tempFolder, bool loopEnabled, float interpFactor)
|
||||
{
|
||||
Logger.Log("Generating frame order information...");
|
||||
|
||||
try
|
||||
{
|
||||
foreach (FileInfo file in IoUtils.GetFileInfosSorted(framesPath.GetParentDir(), false, $"{Paths.frameOrderPrefix}*.*"))
|
||||
foreach (FileInfo file in IoUtils.GetFileInfosSorted(tempFolder, false, $"{Paths.frameOrderPrefix}*.*"))
|
||||
file.Delete();
|
||||
|
||||
benchmark.Restart();
|
||||
await CreateEncFile(framesPath, loopEnabled, times);
|
||||
|
||||
if (Interpolate.currentSettings.ai.NameInternal == Implementations.rifeNcnnVs.NameInternal)
|
||||
CreateFramesFileVid(Interpolate.currentSettings.inPath, Interpolate.currentSettings.tempFolder, loopEnabled, interpFactor);
|
||||
else
|
||||
await CreateFramesFileImgSeq(tempFolder, loopEnabled, interpFactor);
|
||||
|
||||
Logger.Log($"Generating frame order information... Done.", false, true);
|
||||
Logger.Log($"Generated frame order info file in {benchmark.ElapsedMilliseconds} ms", true);
|
||||
}
|
||||
@@ -39,36 +46,122 @@ namespace Flowframes.Main
|
||||
{
|
||||
Logger.Log($"Error generating frame order information: {e.Message}\n{e.StackTrace}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<string, List<string>> dupesDict = new Dictionary<string, List<string>>();
|
||||
static Dictionary<string, List<string>> dupesDict = new Dictionary<string, List<string>>();
|
||||
|
||||
static void LoadDupesFile(string path)
|
||||
{
|
||||
dupesDict = JsonConvert.DeserializeObject<Dictionary<string, List<string>>>(File.ReadAllText(path));
|
||||
}
|
||||
|
||||
public static async Task CreateEncFile(string framesPath, bool loopEnabled, float interpFactor)
|
||||
public static void CreateFramesFileVid(string vidPath, string tempFolder, bool loop, float interpFactor)
|
||||
{
|
||||
if (Interpolate.canceled) return;
|
||||
Logger.Log($"Generating frame order information for {interpFactor}x...", false, true);
|
||||
|
||||
bool loop = Config.GetBool(Config.Key.enableLoop);
|
||||
// frameFileContents.Clear();
|
||||
int lastOutFileCount = 0;
|
||||
|
||||
string inputJsonPath = Path.Combine(tempFolder, "input.json");
|
||||
List<int> inputFrames = JsonConvert.DeserializeObject<List<int>>(File.ReadAllText(inputJsonPath));
|
||||
|
||||
int frameCount = Interpolate.currentMediaFile.FrameCount;
|
||||
frameCount = inputFrames.Count;
|
||||
|
||||
// if (loop)
|
||||
// {
|
||||
// frameCount++;
|
||||
// }
|
||||
|
||||
int frameCountWithoutLast = frameCount - 1;
|
||||
string dupesFile = Path.Combine(tempFolder, "dupes.test.json");
|
||||
var dupes = JsonConvert.DeserializeObject<Dictionary<int, List<int>>>(File.ReadAllText(dupesFile));
|
||||
|
||||
bool debug = Config.GetBool("frameOrderDebug", false);
|
||||
|
||||
int targetFrameCount = (frameCount * interpFactor).RoundToInt() - InterpolateUtils.GetRoundedInterpFramesPerInputFrame(interpFactor);
|
||||
|
||||
Fraction step = new Fraction(frameCount, targetFrameCount + InterpolateUtils.GetRoundedInterpFramesPerInputFrame(interpFactor));
|
||||
|
||||
var framesList = new List<int>();
|
||||
|
||||
for (int i = 0; i < targetFrameCount; i++)
|
||||
{
|
||||
float currentFrameTime = 1 + (step * i).GetFloat();
|
||||
int sourceFrameIdx = (int)Math.Floor(currentFrameTime) - 1;
|
||||
|
||||
framesList.Add(i);
|
||||
Console.WriteLine($"Frame: #{i} - Idx: {sourceFrameIdx} - [Time: {currentFrameTime}]");
|
||||
|
||||
if (sourceFrameIdx < dupes.Count)
|
||||
{
|
||||
bool last = i == lastOutFileCount;
|
||||
|
||||
if (last && loop)
|
||||
continue;
|
||||
|
||||
for (int dupeNum = 0; dupeNum < dupes.ElementAt(sourceFrameIdx).Value.Count; dupeNum++)
|
||||
{
|
||||
framesList.Add(framesList.Last());
|
||||
Console.WriteLine($"Frame: #{i} - Idx: {sourceFrameIdx} - (Dupe {dupeNum + 1}/{dupes.ElementAt(sourceFrameIdx).Value.Count})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if (loop)
|
||||
// {
|
||||
// framesList.Add(framesList.First());
|
||||
// }
|
||||
|
||||
//for (int x = 0; x < frameFileContents.Count; x++)
|
||||
// fileContent += frameFileContents[x];
|
||||
|
||||
lastOutFileCount++;
|
||||
|
||||
if (Config.GetBool(Config.Key.fixOutputDuration)) // Match input duration by padding duping last frame until interp frames == (inputframes * factor)
|
||||
{
|
||||
int neededFrames = (frameCount * interpFactor).RoundToInt() - framesList.Count;
|
||||
|
||||
for (int i = 0; i < neededFrames; i++)
|
||||
framesList.Add(framesList.Last());
|
||||
}
|
||||
|
||||
if (loop)
|
||||
framesList.RemoveAt(framesList.Count() - 1);
|
||||
|
||||
string framesFileVs = Path.Combine(tempFolder, "frames.vs.json");
|
||||
// List<int> frameNums = new List<int>();
|
||||
//
|
||||
// foreach (string line in fileContent.SplitIntoLines().Where(x => x.StartsWith("file ")))
|
||||
// frameNums.Add(line.Split('/')[1].Split('.')[0].GetInt() - 1); // Convert filename to 0-indexed number
|
||||
|
||||
File.WriteAllText(framesFileVs, JsonConvert.SerializeObject(framesList, Formatting.Indented));
|
||||
}
|
||||
|
||||
public static async Task CreateFramesFileImgSeq(string tempFolder, bool loop, float interpFactor)
|
||||
{
|
||||
// await CreateFramesFileVideo(Interpolate.currentSettings.inPath, loop, interpFactor);
|
||||
|
||||
if (Interpolate.canceled) return;
|
||||
Logger.Log($"Generating frame order information for {interpFactor}x...", false, true);
|
||||
|
||||
bool sceneDetection = true;
|
||||
string ext = Interpolate.currentSettings.interpExt;
|
||||
|
||||
frameFileContents.Clear();
|
||||
lastOutFileCount = 0;
|
||||
|
||||
frameFiles = new DirectoryInfo(framesPath).GetFiles("*" + Interpolate.currentSettings.framesExt);
|
||||
string framesDir = Path.Combine(tempFolder, Paths.framesDir);
|
||||
frameFiles = new DirectoryInfo(framesDir).GetFiles("*" + Interpolate.currentSettings.framesExt);
|
||||
frameFilesWithoutLast = frameFiles;
|
||||
Array.Resize(ref frameFilesWithoutLast, frameFilesWithoutLast.Length - 1);
|
||||
string framesFile = Path.Combine(framesPath.GetParentDir(), Paths.GetFrameOrderFilename(interpFactor));
|
||||
string framesFile = Path.Combine(tempFolder, Paths.GetFrameOrderFilename(interpFactor));
|
||||
string fileContent = "";
|
||||
string dupesFile = Path.Combine(framesPath.GetParentDir(), "dupes.json");
|
||||
string dupesFile = Path.Combine(tempFolder, "dupes.json");
|
||||
LoadDupesFile(dupesFile);
|
||||
|
||||
string scnFramesPath = Path.Combine(framesPath.GetParentDir(), Paths.scenesDir);
|
||||
string scnFramesPath = Path.Combine(tempFolder, Paths.scenesDir);
|
||||
|
||||
sceneFrames.Clear();
|
||||
|
||||
@@ -76,7 +169,7 @@ namespace Flowframes.Main
|
||||
sceneFrames = Directory.GetFiles(scnFramesPath).Select(file => GetNameNoExt(file)).ToList();
|
||||
|
||||
inputFilenames.Clear();
|
||||
bool debug = Config.GetBool("frameOrderDebug", false);
|
||||
bool debug = true; // Config.GetBool("frameOrderDebug", false);
|
||||
List<Task> tasks = new List<Task>();
|
||||
int linesPerTask = (400 / interpFactor).RoundToInt();
|
||||
int num = 0;
|
||||
@@ -106,7 +199,7 @@ namespace Flowframes.Main
|
||||
if (Config.GetBool(Config.Key.fixOutputDuration)) // Match input duration by padding duping last frame until interp frames == (inputframes * factor)
|
||||
{
|
||||
int neededFrames = (frameFiles.Length * interpFactor).RoundToInt() - fileContent.SplitIntoLines().Where(x => x.StartsWith("'file ")).Count();
|
||||
|
||||
|
||||
for (int i = 0; i < neededFrames; i++)
|
||||
fileContent += fileContent.SplitIntoLines().Where(x => x.StartsWith("'file ")).Last();
|
||||
}
|
||||
@@ -117,7 +210,7 @@ namespace Flowframes.Main
|
||||
File.WriteAllText(framesFile, fileContent);
|
||||
File.WriteAllText(framesFile + ".inputframes.json", JsonConvert.SerializeObject(inputFilenames, Formatting.Indented));
|
||||
|
||||
string framesFileVs = Path.Combine(framesPath.GetParentDir(), "frames.vs.json");
|
||||
string framesFileVs = Path.Combine(tempFolder, "frames.vs.json");
|
||||
List<int> frameNums = new List<int>();
|
||||
|
||||
foreach (string line in fileContent.SplitIntoLines().Where(x => x.StartsWith("file ")))
|
||||
@@ -194,6 +287,8 @@ namespace Flowframes.Main
|
||||
string inputFilenameFrom = frameFiles[sourceFrameIdx].Name;
|
||||
string inputFilenameTo = (sourceFrameIdx + 1 >= frameFiles.Length) ? "" : frameFiles[sourceFrameIdx + 1].Name;
|
||||
string inputFilenameToNext = (sourceFrameIdx + 2 >= frameFiles.Length) ? "" : frameFiles[sourceFrameIdx + 2].Name;
|
||||
|
||||
Console.WriteLine($"Frame: Idx {sourceFrameIdx} - {(sceneChange && !blendSceneChances ? lastUndiscardFrame : filename)}");
|
||||
lines.Add(new FrameFileLine(sceneChange && !blendSceneChances ? lastUndiscardFrame : filename, inputFilenameFrom, inputFilenameTo, inputFilenameToNext, timestep, sceneChange));
|
||||
|
||||
string inputFilenameNoExtRenamed = Path.GetFileNameWithoutExtension(FrameRename.importFilenames[sourceFrameIdx]);
|
||||
@@ -202,7 +297,11 @@ namespace Flowframes.Main
|
||||
continue;
|
||||
|
||||
foreach (string s in dupesDict[inputFilenameNoExtRenamed])
|
||||
lines.Add(new FrameFileLine(sceneChange && !blendSceneChances ? lastUndiscardFrame : filename, inputFilenameFrom, inputFilenameTo, inputFilenameToNext, timestep, sceneChange));
|
||||
{
|
||||
string fname = sceneChange && !blendSceneChances ? lastUndiscardFrame : filename;
|
||||
Console.WriteLine($"Frame: Idx {sourceFrameIdx} - Dupe {dupesDict[inputFilenameNoExtRenamed].IndexOf(s)}/{dupesDict[inputFilenameNoExtRenamed].Count} {fname}");
|
||||
lines.Add(new FrameFileLine(fname, inputFilenameFrom, inputFilenameTo, inputFilenameToNext, timestep, sceneChange));
|
||||
}
|
||||
}
|
||||
|
||||
if (totalFileCount > lastOutFileCount)
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Flowframes
|
||||
Program.mainForm.SetStatus("Starting...");
|
||||
sw.Restart();
|
||||
|
||||
if (!AutoEncodeResume.resumeNextRun && !(currentSettings.ai.Piped && !currentSettings.inputIsFrames && Config.GetInt(Config.Key.dedupMode) == 0))
|
||||
if (!AutoEncodeResume.resumeNextRun && !(currentSettings.ai.Piped && !currentSettings.inputIsFrames /* && Config.GetInt(Config.Key.dedupMode) == 0) */))
|
||||
{
|
||||
await GetFrames();
|
||||
if (canceled) return;
|
||||
@@ -180,7 +180,7 @@ namespace Flowframes
|
||||
|
||||
if (!Config.GetBool(Config.Key.enableLoop))
|
||||
{
|
||||
await Utils.CopyLastFrame(currentMediaFile.FrameCount);
|
||||
// await Utils.CopyLastFrame(currentMediaFile.FrameCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -199,14 +199,18 @@ namespace Flowframes
|
||||
|
||||
bool dedupe = Config.GetInt(Config.Key.dedupMode) != 0;
|
||||
|
||||
if (!ai.Piped || (ai.Piped && currentSettings.inputIsFrames) || (ai.Piped && dedupe))
|
||||
if (!ai.Piped || (ai.Piped && currentSettings.inputIsFrames))
|
||||
{
|
||||
await Task.Run(async () => { await Dedupe.CreateDupesFile(currentSettings.framesFolder, currentMediaFile.FrameCount, currentSettings.framesExt); });
|
||||
await Task.Run(async () => { await Dedupe.CreateDupesFile(currentSettings.framesFolder, currentSettings.framesExt); });
|
||||
await Task.Run(async () => { await FrameRename.Rename(); });
|
||||
}
|
||||
else if (ai.Piped && dedupe)
|
||||
{
|
||||
await Task.Run(async () => { await Dedupe.CreateFramesFileVideo(currentSettings.inPath, Config.GetBool(Config.Key.enableLoop)); });
|
||||
}
|
||||
|
||||
if (!ai.Piped || (ai.Piped && dedupe))
|
||||
await Task.Run(async () => { await FrameOrder.CreateFrameOrderFile(currentSettings.framesFolder, Config.GetBool(Config.Key.enableLoop), currentSettings.interpFactor); });
|
||||
await Task.Run(async () => { await FrameOrder.CreateFrameOrderFile(currentSettings.tempFolder, Config.GetBool(Config.Key.enableLoop), currentSettings.interpFactor); });
|
||||
|
||||
if (currentSettings.model.FixedFactors.Count() > 0 && (currentSettings.interpFactor != (int)currentSettings.interpFactor || !currentSettings.model.FixedFactors.Contains(currentSettings.interpFactor.RoundToInt())))
|
||||
Cancel($"The selected model does not support {currentSettings.interpFactor}x interpolation.\n\nSupported Factors: {currentSettings.model.GetFactorsString()}");
|
||||
|
||||
@@ -7,8 +7,6 @@ using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flowframes.Os
|
||||
{
|
||||
@@ -58,7 +56,7 @@ namespace Flowframes.Os
|
||||
l.Add($"inputPath = r'{inputPath}'");
|
||||
l.Add($"");
|
||||
|
||||
bool loadFrames = s.InterpSettings.inputIsFrames || (s.Dedupe && !s.Realtime);
|
||||
bool loadFrames = s.InterpSettings.inputIsFrames;
|
||||
|
||||
if (loadFrames)
|
||||
{
|
||||
@@ -74,6 +72,8 @@ namespace Flowframes.Os
|
||||
l.Add($"if os.path.isdir(r'{s.InterpSettings.tempFolder}'):");
|
||||
l.Add($" indexFilePath = r'{Path.Combine(s.InterpSettings.tempFolder, "cache.lwi")}'");
|
||||
l.Add($"clip = core.lsmas.LWLibavSource(inputPath, cachefile=indexFilePath)"); // Load video with lsmash
|
||||
l.Add("clip = core.text.FrameNum(clip, alignment=7)");
|
||||
l.Add(GetDedupeLines(s));
|
||||
}
|
||||
|
||||
if (trim)
|
||||
@@ -187,6 +187,30 @@ namespace Flowframes.Os
|
||||
return s;
|
||||
}
|
||||
|
||||
static string GetDedupeLines(VsSettings settings)
|
||||
{
|
||||
string s = "";
|
||||
|
||||
string inputJsonPath = Path.Combine(settings.InterpSettings.tempFolder, "input.json");
|
||||
|
||||
if (!File.Exists(inputJsonPath))
|
||||
return s;
|
||||
|
||||
s += "reorderedClip = clip[0]\n";
|
||||
s += "\n";
|
||||
s += $"with open(r'{inputJsonPath}') as json_file:\n";
|
||||
s += " frameList = json.load(json_file)\n";
|
||||
s += " \n";
|
||||
s += " for i in frameList:\n";
|
||||
s += " reorderedClip = reorderedClip + clip[i]\n";
|
||||
s += "\n";
|
||||
s += "clip = reorderedClip.std.Trim(1, reorderedClip.num_frames - 1)\n";
|
||||
s += "clip = core.text.FrameNum(clip, alignment=4)\n";
|
||||
s += "\n";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static string GetRedupeLines(VsSettings settings)
|
||||
{
|
||||
string s = "";
|
||||
@@ -201,6 +225,7 @@ namespace Flowframes.Os
|
||||
s += " reorderedClip = reorderedClip + clip[i]\n";
|
||||
s += "\n";
|
||||
s += "clip = reorderedClip.std.Trim(1, reorderedClip.num_frames - 1)\n";
|
||||
s += "clip = core.text.FrameNum(clip, alignment=1)\n";
|
||||
s += "\n";
|
||||
|
||||
return s;
|
||||
|
||||
Reference in New Issue
Block a user