mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2026-01-08 02:09:31 -06:00
126 lines
4.2 KiB
C#
126 lines
4.2 KiB
C#
namespace FileFlows.VideoNodes
|
|
{
|
|
using FileFlows.Plugin;
|
|
using FileFlows.Plugin.Attributes;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
public class AutoChapters: EncodingNode
|
|
{
|
|
public override int Outputs => 2;
|
|
|
|
|
|
[NumberInt(1)]
|
|
[DefaultValue(60)]
|
|
public int MinimumLength { get; set; } = 60;
|
|
|
|
[NumberInt(2)]
|
|
[DefaultValue(45)]
|
|
public int Percent { get; set; } = 45;
|
|
|
|
public override int Execute(NodeParameters args)
|
|
{
|
|
VideoInfo videoInfo = GetVideoInfo(args);
|
|
if (videoInfo == null)
|
|
return -1;
|
|
|
|
if (videoInfo.Chapters?.Count > 3)
|
|
{
|
|
args.Logger.ILog(videoInfo.Chapters.Count + " chapters already detected in file");
|
|
return 2;
|
|
}
|
|
|
|
string tempMetaDataFile = GenerateMetaDataFile(this, args, videoInfo, FFMPEG, this.Percent, this.MinimumLength);
|
|
if (string.IsNullOrEmpty(tempMetaDataFile))
|
|
return 2;
|
|
|
|
string[] ffArgs = new[] { "-i", tempMetaDataFile, "-map_metadata", "1", "-codec", "copy", "-max_muxing_queue_size", "1024" };
|
|
if (Encode(args, FFMPEG, ffArgs.ToList()))
|
|
{
|
|
args.Logger?.ILog($"Adding chapters to file");
|
|
return 1;
|
|
}
|
|
args.Logger?.ELog("Processing failed");
|
|
return -1;
|
|
}
|
|
|
|
internal static string GenerateMetaDataFile(EncodingNode node, NodeParameters args, VideoInfo videoInfo, string ffmpegExe, int percent, int minimumLength)
|
|
{
|
|
string output;
|
|
var result = node.Encode(args, ffmpegExe, new List<string>
|
|
{
|
|
"-hide_banner",
|
|
"-i", args.WorkingFile,
|
|
"-filter:v", $"select='gt(scene,{percent / 100f})',showinfo",
|
|
"-f", "null",
|
|
"-"
|
|
}, out output, updateWorkingFile: false, dontAddInputFile: true);
|
|
|
|
if (result == false)
|
|
{
|
|
args.Logger?.WLog("Failed to detect scenes");
|
|
return string.Empty;
|
|
}
|
|
|
|
|
|
if (minimumLength < 30)
|
|
{
|
|
args.Logger?.ILog("Mimium length set to invalid number, defaulting to 60 seconds");
|
|
minimumLength = 60;
|
|
}
|
|
else
|
|
{
|
|
args.Logger?.ILog($"Minimum length of chapter {minimumLength} seconds");
|
|
}
|
|
|
|
StringBuilder metadata = new StringBuilder();
|
|
metadata.AppendLine(";FFMETADATA1");
|
|
metadata.AppendLine("");
|
|
|
|
int chapter = 0;
|
|
|
|
TimeSpan previous = TimeSpan.Zero;
|
|
foreach (Match match in Regex.Matches(output, @"(?<=(pts_time:))[\d]+\.[\d]+"))
|
|
{
|
|
TimeSpan time = TimeSpan.FromSeconds(double.Parse(match.Value));
|
|
if (Math.Abs((time - previous).TotalSeconds) < minimumLength)
|
|
continue;
|
|
|
|
AddChapter(previous, time);
|
|
previous = time;
|
|
}
|
|
|
|
var totalTime = TimeSpan.FromSeconds(videoInfo.VideoStreams[0].Duration.TotalSeconds);
|
|
if (Math.Abs((totalTime - previous).TotalSeconds) > minimumLength)
|
|
AddChapter(previous, totalTime);
|
|
|
|
if (chapter == 0)
|
|
{
|
|
args.Logger?.ILog("Failed to detect any scene changes");
|
|
return string.Empty;
|
|
}
|
|
|
|
string tempMetaDataFile = Path.Combine(args.TempPath, Guid.NewGuid().ToString() + ".txt");
|
|
File.WriteAllText(tempMetaDataFile, metadata.ToString());
|
|
|
|
return tempMetaDataFile;
|
|
|
|
void AddChapter(TimeSpan start, TimeSpan end)
|
|
{
|
|
|
|
metadata.AppendLine("[CHAPTER]");
|
|
metadata.AppendLine("TIMEBASE=1/1000");
|
|
metadata.AppendLine("START=" + ((int)(start.TotalSeconds * 1000)));
|
|
metadata.AppendLine("END=" + ((int)(end.TotalSeconds * 1000)));
|
|
metadata.AppendLine("title=Chapter " + (++chapter));
|
|
metadata.AppendLine();
|
|
}
|
|
}
|
|
}
|
|
}
|