mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2026-02-14 07:18:27 -06:00
Added AutoChapters node
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
namespace FileFlows.VideoNodes
|
||||
{
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
internal static class ExtensionMethods
|
||||
{
|
||||
public static void AddOrUpdate(this Dictionary<string, object> dict, string key, object value) {
|
||||
@@ -14,6 +16,12 @@
|
||||
return str == string.Empty ? null : str;
|
||||
}
|
||||
|
||||
public static bool TryMatch(this Regex regex, string input, out Match match)
|
||||
{
|
||||
match = regex.Match(input);
|
||||
return match.Success;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> SplitCommandLine(this string commandLine)
|
||||
{
|
||||
bool inQuotes = false;
|
||||
|
||||
39
VideoNodes/Tests/AutoChaptersTests.cs
Normal file
39
VideoNodes/Tests/AutoChaptersTests.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
#if(DEBUG)
|
||||
|
||||
namespace VideoNodes.Tests
|
||||
{
|
||||
using FileFlows.VideoNodes;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
[TestClass]
|
||||
public class AutoChaptersTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void AutoChaptersTests_Test_01()
|
||||
{
|
||||
const string file = @"D:\videos\unprocessed\The IT Crowd - 2x04 - The Dinner Party - No English.mkv";
|
||||
var vi = new VideoInfoHelper(@"C:\utils\ffmpeg\ffmpeg.exe", new TestLogger());
|
||||
var vii = vi.Read(file);
|
||||
|
||||
AutoChapters node = new();
|
||||
//node.OutputFile = file + ".sup";
|
||||
var args = new FileFlows.Plugin.NodeParameters(file, new TestLogger(), false, string.Empty);
|
||||
args.GetToolPathActual = (string tool) => @"C:\utils\ffmpeg\ffmpeg.exe";
|
||||
args.TempPath = @"D:\videos\temp";
|
||||
|
||||
new VideoFile().Execute(args);
|
||||
|
||||
int output = node.Execute(args);
|
||||
|
||||
Assert.AreEqual(1, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -392,6 +392,429 @@ namespace VideoNodes.Tests
|
||||
Assert.AreEqual(1920, vi.VideoStreams[0].Width);
|
||||
Assert.AreEqual(1080, vi.VideoStreams[0].Height);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void VideoInfoTest_Chapters()
|
||||
{
|
||||
string ffmpegOutput = @"[matroska,webm @ 00000263322abdc0] Could not find codec parameters for stream 3 (Subtitle: hdmv_pgs_subtitle (pgssub)): unspecified size
|
||||
Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
|
||||
[matroska,webm @ 00000263322abdc0] Could not find codec parameters for stream 4 (Subtitle: hdmv_pgs_subtitle (pgssub)): unspecified size
|
||||
Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
|
||||
Input #0, matroska,webm, from 'D:\downloads\sabnzbd\complete\movies\Cast.Away.2000.BluRay.1080p.REMUX.AVC.DTS-HD.MA.5.1-LEGi0N\b0e4afee2ced4ae3a3592b82ae335608.mkv':
|
||||
Metadata:
|
||||
encoder : libebml v1.4.2 + libmatroska v1.6.4
|
||||
creation_time : 2022-02-02T22:32:47.000000Z
|
||||
Duration: 02:23:46.66, start: 0.000000, bitrate: 38174 kb/s
|
||||
Chapters:
|
||||
Chapter #0:0: start 0.000000, end 110.819000
|
||||
Metadata:
|
||||
title : Chapter 01
|
||||
Chapter #0:1: start 110.819000, end 517.851000
|
||||
Metadata:
|
||||
title : Chapter 02
|
||||
Chapter #0:2: start 517.851000, end 743.326000
|
||||
Metadata:
|
||||
title : Chapter 03
|
||||
Chapter #0:3: start 743.326000, end 1061.269000
|
||||
Metadata:
|
||||
title : Chapter 04
|
||||
Chapter #0:4: start 1061.269000, end 1243.534000
|
||||
Metadata:
|
||||
title : Chapter 05
|
||||
Chapter #0:5: start 1243.534000, end 1360.234000
|
||||
Metadata:
|
||||
title : Chapter 06
|
||||
Chapter #0:6: start 1360.234000, end 1545.461000
|
||||
Metadata:
|
||||
title : Chapter 07
|
||||
Chapter #0:7: start 1545.461000, end 1871.620000
|
||||
Metadata:
|
||||
title : Chapter 08
|
||||
Chapter #0:8: start 1871.620000, end 2155.320000
|
||||
Metadata:
|
||||
title : Chapter 09
|
||||
Chapter #0:9: start 2155.320000, end 2375.623000
|
||||
Metadata:
|
||||
title : Chapter 10
|
||||
Chapter #0:10: start 2375.623000, end 2543.207000
|
||||
Metadata:
|
||||
title : Chapter 11
|
||||
Chapter #0:11: start 2543.207000, end 2794.208000
|
||||
Metadata:
|
||||
title : Chapter 12
|
||||
Chapter #0:12: start 2794.208000, end 3109.314000
|
||||
Metadata:
|
||||
title : Chapter 13
|
||||
Chapter #0:13: start 3109.314000, end 3389.052000
|
||||
Metadata:
|
||||
title : Chapter 14
|
||||
Chapter #0:14: start 3389.052000, end 3694.357000
|
||||
Metadata:
|
||||
title : Chapter 15
|
||||
Chapter #0:15: start 3694.357000, end 3873.119000
|
||||
Metadata:
|
||||
title : Chapter 16
|
||||
Chapter #0:16: start 3873.119000, end 4391.846000
|
||||
Metadata:
|
||||
title : Chapter 17
|
||||
Chapter #0:17: start 4391.846000, end 4657.736000
|
||||
Metadata:
|
||||
title : Chapter 18
|
||||
Chapter #0:18: start 4657.736000, end 4749.745000
|
||||
Metadata:
|
||||
title : Chapter 19
|
||||
Chapter #0:19: start 4749.745000, end 4842.045000
|
||||
Metadata:
|
||||
title : Chapter 20
|
||||
Chapter #0:20: start 4842.045000, end 5197.901000
|
||||
Metadata:
|
||||
title : Chapter 21
|
||||
Chapter #0:21: start 5197.901000, end 5640.176000
|
||||
Metadata:
|
||||
title : Chapter 22
|
||||
Chapter #0:22: start 5640.176000, end 6037.365000
|
||||
Metadata:
|
||||
title : Chapter 23
|
||||
Chapter #0:23: start 6037.365000, end 6321.398000
|
||||
Metadata:
|
||||
title : Chapter 24
|
||||
Chapter #0:24: start 6321.398000, end 6458.368000
|
||||
Metadata:
|
||||
title : Chapter 25
|
||||
Chapter #0:25: start 6458.368000, end 6810.470000
|
||||
Metadata:
|
||||
title : Chapter 26
|
||||
Chapter #0:26: start 6810.470000, end 6959.953000
|
||||
Metadata:
|
||||
title : Chapter 27
|
||||
Chapter #0:27: start 6959.953000, end 7499.575000
|
||||
Metadata:
|
||||
title : Chapter 28
|
||||
Chapter #0:28: start 7499.575000, end 7707.575000
|
||||
Metadata:
|
||||
title : Chapter 29
|
||||
Chapter #0:29: start 7707.575000, end 7941.725000
|
||||
Metadata:
|
||||
title : Chapter 30
|
||||
Chapter #0:30: start 7941.725000, end 8214.414000
|
||||
Metadata:
|
||||
title : Chapter 31
|
||||
Chapter #0:31: start 8214.414000, end 8626.656000
|
||||
Metadata:
|
||||
title : Chapter 32
|
||||
Stream #0:0(eng): Video: h264 (High), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc (default)
|
||||
Metadata:
|
||||
title : English
|
||||
BPS : 33666894
|
||||
DURATION : 02:23:46.618000000
|
||||
NUMBER_OF_FRAMES: 206832
|
||||
NUMBER_OF_BYTES : 36303929846
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:1(eng): Audio: dts (DTS-HD MA), 48000 Hz, 5.1(side), s32p (24 bit) (default)
|
||||
Metadata:
|
||||
title : English
|
||||
BPS : 4236399
|
||||
DURATION : 02:23:46.624000000
|
||||
NUMBER_OF_FRAMES: 808746
|
||||
NUMBER_OF_BYTES : 4568228448
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:2(eng): Audio: ac3, 48000 Hz, stereo, fltp, 224 kb/s (comment)
|
||||
Metadata:
|
||||
title : English commentary
|
||||
BPS : 224000
|
||||
DURATION : 02:23:46.656000000
|
||||
NUMBER_OF_FRAMES: 269583
|
||||
NUMBER_OF_BYTES : 241546368
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:3(eng): Subtitle: hdmv_pgs_subtitle
|
||||
Metadata:
|
||||
title : English (SDH)
|
||||
BPS : 25275
|
||||
DURATION : 02:14:32.439000000
|
||||
NUMBER_OF_FRAMES: 1740
|
||||
NUMBER_OF_BYTES : 25504616
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:4(spa): Subtitle: hdmv_pgs_subtitle
|
||||
Metadata:
|
||||
title : Spanish
|
||||
BPS : 21585
|
||||
DURATION : 02:12:54.884000000
|
||||
NUMBER_OF_FRAMES: 1412
|
||||
NUMBER_OF_BYTES : 21517695
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES";
|
||||
var vi = VideoInfoHelper.ParseOutput(null, ffmpegOutput);
|
||||
Assert.AreEqual(32, vi.Chapters?.Count ?? 0);
|
||||
Assert.AreEqual("Chapter 32", vi.Chapters[31].Title);
|
||||
Assert.AreEqual(TimeSpan.FromSeconds(8214.414000), vi.Chapters[31].Start);
|
||||
Assert.AreEqual(TimeSpan.FromSeconds(8626.656000), vi.Chapters[31].End);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void VideoInfoTest_Chapters_NoStart()
|
||||
{
|
||||
string ffmpegOutput = @"[matroska,webm @ 00000263322abdc0] Could not find codec parameters for stream 3 (Subtitle: hdmv_pgs_subtitle (pgssub)): unspecified size
|
||||
Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
|
||||
[matroska,webm @ 00000263322abdc0] Could not find codec parameters for stream 4 (Subtitle: hdmv_pgs_subtitle (pgssub)): unspecified size
|
||||
Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
|
||||
Input #0, matroska,webm, from 'D:\downloads\sabnzbd\complete\movies\Cast.Away.2000.BluRay.1080p.REMUX.AVC.DTS-HD.MA.5.1-LEGi0N\b0e4afee2ced4ae3a3592b82ae335608.mkv':
|
||||
Metadata:
|
||||
encoder : libebml v1.4.2 + libmatroska v1.6.4
|
||||
creation_time : 2022-02-02T22:32:47.000000Z
|
||||
Duration: 02:23:46.66, start: 0.000000, bitrate: 38174 kb/s
|
||||
Chapters:
|
||||
Chapter #0:0: end 110.819000
|
||||
Metadata:
|
||||
title : Chapter 01
|
||||
Chapter #0:1: start 110.819000, end 517.851000
|
||||
Metadata:
|
||||
title : Chapter 02
|
||||
Chapter #0:2: start 517.851000, end 743.326000
|
||||
Metadata:
|
||||
title : Chapter 03
|
||||
Chapter #0:3: start 743.326000, end 1061.269000
|
||||
Metadata:
|
||||
title : Chapter 04
|
||||
Chapter #0:4: start 1061.269000, end 1243.534000
|
||||
Metadata:
|
||||
title : Chapter 05
|
||||
Chapter #0:5: start 1243.534000, end 1360.234000
|
||||
Metadata:
|
||||
title : Chapter 06
|
||||
Chapter #0:6: start 1360.234000, end 1545.461000
|
||||
Metadata:
|
||||
title : Chapter 07
|
||||
Chapter #0:7: start 1545.461000, end 1871.620000
|
||||
Metadata:
|
||||
title : Chapter 08
|
||||
Chapter #0:8: start 1871.620000, end 2155.320000
|
||||
Metadata:
|
||||
title : Chapter 09
|
||||
Chapter #0:9: start 2155.320000, end 2375.623000
|
||||
Metadata:
|
||||
title : Chapter 10
|
||||
Chapter #0:10: start 2375.623000, end 2543.207000
|
||||
Metadata:
|
||||
title : Chapter 11
|
||||
Chapter #0:11: start 2543.207000, end 2794.208000
|
||||
Metadata:
|
||||
title : Chapter 12
|
||||
Chapter #0:12: start 2794.208000, end 3109.314000
|
||||
Metadata:
|
||||
title : Chapter 13
|
||||
Chapter #0:13: start 3109.314000, end 3389.052000
|
||||
Metadata:
|
||||
title : Chapter 14
|
||||
Chapter #0:14: start 3389.052000, end 3694.357000
|
||||
Metadata:
|
||||
title : Chapter 15
|
||||
Chapter #0:15: start 3694.357000, end 3873.119000
|
||||
Metadata:
|
||||
title : Chapter 16
|
||||
Chapter #0:16: start 3873.119000, end 4391.846000
|
||||
Metadata:
|
||||
title : Chapter 17
|
||||
Chapter #0:17: start 4391.846000, end 4657.736000
|
||||
Metadata:
|
||||
title : Chapter 18
|
||||
Chapter #0:18: start 4657.736000, end 4749.745000
|
||||
Metadata:
|
||||
title : Chapter 19
|
||||
Chapter #0:19: start 4749.745000, end 4842.045000
|
||||
Metadata:
|
||||
title : Chapter 20
|
||||
Chapter #0:20: start 4842.045000, end 5197.901000
|
||||
Metadata:
|
||||
title : Chapter 21
|
||||
Chapter #0:21: start 5197.901000, end 5640.176000
|
||||
Metadata:
|
||||
title : Chapter 22
|
||||
Chapter #0:22: start 5640.176000, end 6037.365000
|
||||
Metadata:
|
||||
title : Chapter 23
|
||||
Chapter #0:23: start 6037.365000, end 6321.398000
|
||||
Metadata:
|
||||
title : Chapter 24
|
||||
Chapter #0:24: start 6321.398000, end 6458.368000
|
||||
Metadata:
|
||||
title : Chapter 25
|
||||
Chapter #0:25: start 6458.368000, end 6810.470000
|
||||
Metadata:
|
||||
title : Chapter 26
|
||||
Chapter #0:26: start 6810.470000, end 6959.953000
|
||||
Metadata:
|
||||
title : Chapter 27
|
||||
Chapter #0:27: start 6959.953000, end 7499.575000
|
||||
Metadata:
|
||||
title : Chapter 28
|
||||
Chapter #0:28: start 7499.575000, end 7707.575000
|
||||
Metadata:
|
||||
title : Chapter 29
|
||||
Chapter #0:29: start 7707.575000, end 7941.725000
|
||||
Metadata:
|
||||
title : Chapter 30
|
||||
Chapter #0:30: start 7941.725000, end 8214.414000
|
||||
Metadata:
|
||||
title : Chapter 31
|
||||
Chapter #0:31: start 8214.414000, end 8626.656000
|
||||
Metadata:
|
||||
title : Chapter 32
|
||||
Stream #0:0(eng): Video: h264 (High), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc (default)
|
||||
Metadata:
|
||||
title : English
|
||||
BPS : 33666894
|
||||
DURATION : 02:23:46.618000000
|
||||
NUMBER_OF_FRAMES: 206832
|
||||
NUMBER_OF_BYTES : 36303929846
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:1(eng): Audio: dts (DTS-HD MA), 48000 Hz, 5.1(side), s32p (24 bit) (default)
|
||||
Metadata:
|
||||
title : English
|
||||
BPS : 4236399
|
||||
DURATION : 02:23:46.624000000
|
||||
NUMBER_OF_FRAMES: 808746
|
||||
NUMBER_OF_BYTES : 4568228448
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:2(eng): Audio: ac3, 48000 Hz, stereo, fltp, 224 kb/s (comment)
|
||||
Metadata:
|
||||
title : English commentary
|
||||
BPS : 224000
|
||||
DURATION : 02:23:46.656000000
|
||||
NUMBER_OF_FRAMES: 269583
|
||||
NUMBER_OF_BYTES : 241546368
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:3(eng): Subtitle: hdmv_pgs_subtitle
|
||||
Metadata:
|
||||
title : English (SDH)
|
||||
BPS : 25275
|
||||
DURATION : 02:14:32.439000000
|
||||
NUMBER_OF_FRAMES: 1740
|
||||
NUMBER_OF_BYTES : 25504616
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:4(spa): Subtitle: hdmv_pgs_subtitle
|
||||
Metadata:
|
||||
title : Spanish
|
||||
BPS : 21585
|
||||
DURATION : 02:12:54.884000000
|
||||
NUMBER_OF_FRAMES: 1412
|
||||
NUMBER_OF_BYTES : 21517695
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES";
|
||||
var vi = VideoInfoHelper.ParseOutput(null, ffmpegOutput);
|
||||
Assert.AreEqual(32, vi.Chapters?.Count ?? 0);
|
||||
Assert.AreEqual(TimeSpan.FromSeconds(0), vi.Chapters[0].Start);
|
||||
Assert.AreEqual(TimeSpan.FromSeconds(110.819000), vi.Chapters[0].End);
|
||||
}
|
||||
[TestMethod]
|
||||
public void VideoInfoTest_Chapters_Bad()
|
||||
{
|
||||
string ffmpegOutput = @"[matroska,webm @ 00000263322abdc0] Could not find codec parameters for stream 3 (Subtitle: hdmv_pgs_subtitle (pgssub)): unspecified size
|
||||
Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
|
||||
[matroska,webm @ 00000263322abdc0] Could not find codec parameters for stream 4 (Subtitle: hdmv_pgs_subtitle (pgssub)): unspecified size
|
||||
Consider increasing the value for the 'analyzeduration' (0) and 'probesize' (5000000) options
|
||||
Input #0, matroska,webm, from 'D:\downloads\sabnzbd\complete\movies\Cast.Away.2000.BluRay.1080p.REMUX.AVC.DTS-HD.MA.5.1-LEGi0N\b0e4afee2ced4ae3a3592b82ae335608.mkv':
|
||||
Metadata:
|
||||
encoder : libebml v1.4.2 + libmatroska v1.6.4
|
||||
creation_time : 2022-02-02T22:32:47.000000Z
|
||||
Duration: 02:23:46.66, start: 0.000000, bitrate: 38174 kb/s
|
||||
Chapters:
|
||||
Chapter #0:0: end 110.819000
|
||||
Metadata:
|
||||
title : Chapter 01
|
||||
Chapter #0:1: start 110.819000, end 517.851000
|
||||
Metadata:
|
||||
title : Chapter 0200, end 5640.176000
|
||||
Metadata:
|
||||
title : Chapter 2200, end 7499.575000
|
||||
Metadata:
|
||||
title : Chapter 28
|
||||
Chapter #0:28: start 7499.575000, end 7707.575000
|
||||
Metadata:
|
||||
title : Chapter 29
|
||||
Chapter #0:29: start 7707.575000, end 7941.725000
|
||||
Metadata:
|
||||
title : Chapter 30
|
||||
Chapter #0:30: start 7941.725000, end 8214.414000
|
||||
Metadata:
|
||||
title : Chapter 31
|
||||
Chapter #0:31: start 8214.414000, end 8626.656000
|
||||
Metadata:
|
||||
title : Chapter 32
|
||||
Stream #0:0(eng): Video: h264 (High), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc (default)
|
||||
Metadata:
|
||||
title : English
|
||||
BPS : 33666894
|
||||
DURATION : 02:23:46.618000000
|
||||
NUMBER_OF_FRAMES: 206832
|
||||
NUMBER_OF_BYTES : 36303929846
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:1(eng): Audio: dts (DTS-HD MA), 48000 Hz, 5.1(side), s32p (24 bit) (default)
|
||||
Metadata:
|
||||
title : English
|
||||
BPS : 4236399
|
||||
DURATION : 02:23:46.624000000
|
||||
NUMBER_OF_FRAMES: 808746
|
||||
NUMBER_OF_BYTES : 4568228448
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:2(eng): Audio: ac3, 48000 Hz, stereo, fltp, 224 kb/s (comment)
|
||||
Metadata:
|
||||
title : English commentary
|
||||
BPS : 224000
|
||||
DURATION : 02:23:46.656000000
|
||||
NUMBER_OF_FRAMES: 269583
|
||||
NUMBER_OF_BYTES : 241546368
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:3(eng): Subtitle: hdmv_pgs_subtitle
|
||||
Metadata:
|
||||
title : English (SDH)
|
||||
BPS : 25275
|
||||
DURATION : 02:14:32.439000000
|
||||
NUMBER_OF_FRAMES: 1740
|
||||
NUMBER_OF_BYTES : 25504616
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES
|
||||
Stream #0:4(spa): Subtitle: hdmv_pgs_subtitle
|
||||
Metadata:
|
||||
title : Spanish
|
||||
BPS : 21585
|
||||
DURATION : 02:12:54.884000000
|
||||
NUMBER_OF_FRAMES: 1412
|
||||
NUMBER_OF_BYTES : 21517695
|
||||
_STATISTICS_WRITING_APP: mkvmerge v64.0.0 ('Willows') 64-bit
|
||||
_STATISTICS_WRITING_DATE_UTC: 2022-02-02 22:32:47
|
||||
_STATISTICS_TAGS: BPS DURATION NUMBER_OF_FRAMES NUMBER_OF_BYTES";
|
||||
var vi = VideoInfoHelper.ParseOutput(null, ffmpegOutput);
|
||||
Assert.AreEqual(6, vi.Chapters?.Count ?? 0);
|
||||
Assert.AreEqual("Chapter 29", vi.Chapters[2].Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace FileFlows.VideoNodes
|
||||
public List<VideoStream> VideoStreams { get; set; } = new List<VideoStream>();
|
||||
public List<AudioStream> AudioStreams { get; set; } = new List<AudioStream>();
|
||||
public List<SubtitleStream> SubtitleStreams { get; set; } = new List<SubtitleStream>();
|
||||
|
||||
public List<Chapter> Chapters { get; set; } = new List<Chapter>();
|
||||
}
|
||||
|
||||
public class VideoFileStream
|
||||
@@ -96,4 +98,11 @@ namespace FileFlows.VideoNodes
|
||||
/// </summary>
|
||||
public bool Forced { get; set; }
|
||||
}
|
||||
|
||||
public class Chapter
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public TimeSpan Start { get; set; }
|
||||
public TimeSpan End { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace FileFlows.VideoNodes
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -89,6 +90,8 @@ namespace FileFlows.VideoNodes
|
||||
vi.Bitrate = float.Parse(brMatch.Value) * 1_000; // to convert to b/s
|
||||
}
|
||||
|
||||
vi.Chapters = ParseChapters(output);
|
||||
|
||||
int subtitleIndex = 1;
|
||||
foreach (Match sm in streamMatches)
|
||||
{
|
||||
@@ -135,6 +138,54 @@ namespace FileFlows.VideoNodes
|
||||
return vi;
|
||||
}
|
||||
|
||||
private static List<Chapter> ParseChapters(string output)
|
||||
{
|
||||
try
|
||||
{
|
||||
var rgxChatpers = new Regex("(?<=(Chapters:))(.*?)(?=(Stream))", RegexOptions.Singleline);
|
||||
string strChapters;
|
||||
if (rgxChatpers.TryMatch(output, out Match matchChapters))
|
||||
strChapters = matchChapters.Value.Trim();
|
||||
else
|
||||
return new List<Chapter>();
|
||||
|
||||
var rgxChapter = new Regex("Chapter #(.*?)(?=(Chapter #|$))", RegexOptions.Singleline);
|
||||
var chapters = new List<Chapter>();
|
||||
|
||||
var rgxTitle = new Regex(@"title[\s]*:[\s]*(.*?)$");
|
||||
var rgxStart = new Regex(@"(?<=(start[\s]))[\d]+\.[\d]+");
|
||||
var rgxEnd = new Regex(@"(?<=(end[\s]))[\d]+\.[\d]+");
|
||||
foreach (Match match in rgxChapter.Matches(strChapters))
|
||||
{
|
||||
try
|
||||
{
|
||||
Chapter chapter = new Chapter();
|
||||
if (rgxTitle.TryMatch(match.Value.Trim(), out Match title))
|
||||
chapter.Title = title.Groups[1].Value;
|
||||
|
||||
if (rgxStart.TryMatch(match.Value, out Match start))
|
||||
{
|
||||
double startSeconds = double.Parse(start.Value);
|
||||
chapter.Start = TimeSpan.FromSeconds(startSeconds);
|
||||
}
|
||||
if (rgxEnd.TryMatch(match.Value, out Match end))
|
||||
{
|
||||
double endSeconds = double.Parse(end.Value);
|
||||
chapter.End = TimeSpan.FromSeconds(endSeconds);
|
||||
}
|
||||
|
||||
if (chapter.Start > TimeSpan.Zero || chapter.End > TimeSpan.Zero)
|
||||
{
|
||||
chapters.Add(chapter);
|
||||
}
|
||||
}
|
||||
catch (Exception ) { }
|
||||
}
|
||||
|
||||
return chapters;
|
||||
}catch (Exception) { return new List<Chapter>(); }
|
||||
}
|
||||
|
||||
public static VideoStream ParseVideoStream(ILogger logger, string info, string fullOutput)
|
||||
{
|
||||
// Stream #0:0(eng): Video: h264 (High), yuv420p(tv, bt709/unknown/unknown, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn (default)
|
||||
|
||||
Binary file not shown.
@@ -67,6 +67,21 @@
|
||||
"Language-Help": "The ISO 639-2 language code to use. \nhttps://en.wikipedia.org/wiki/List_of_ISO_639-2_codes"
|
||||
}
|
||||
},
|
||||
"AutoChapters": {
|
||||
"Description": "Automatically detect scene changes in the video to generate chapters.",
|
||||
"Outputs": {
|
||||
"1": "Chapters generated and saved to temporary file",
|
||||
"2": "No chapters detected or video already had chapters"
|
||||
},
|
||||
"Fields": {
|
||||
"MinimumLength": "Minimum Length",
|
||||
"MinimumLength-Suffix": "seconds",
|
||||
"MinimumLength-Help": "The minimum length of a chapter in seconds",
|
||||
"Percent": "Percent",
|
||||
"Percent-Suffix": "%",
|
||||
"Percent-Help": "The threshold percentage value to use for scene detection changes. A good value is 45%"
|
||||
}
|
||||
},
|
||||
"ComskipChapters": {
|
||||
"Description": "Uses a comskip EDL file and will create chapters given that EDL comskip file.",
|
||||
"Outputs": {
|
||||
|
||||
118
VideoNodes/VideoNodes/AutoChapters.cs
Normal file
118
VideoNodes/VideoNodes/AutoChapters.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
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)
|
||||
{
|
||||
string ffmpegExe = GetFFMpegExe(args);
|
||||
if (string.IsNullOrEmpty(ffmpegExe))
|
||||
return -1;
|
||||
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 output;
|
||||
var result = 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 2;
|
||||
}
|
||||
|
||||
|
||||
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("No ads found in edl file");
|
||||
return 2;
|
||||
}
|
||||
|
||||
string tempMetaDataFile = Path.Combine(args.TempPath, Guid.NewGuid().ToString() + ".txt");
|
||||
File.WriteAllText(tempMetaDataFile, metadata.ToString());
|
||||
|
||||
string[] ffArgs = new[] { "-i", tempMetaDataFile, "-map_metadata", "1", "-codec", "copy", "-max_muxing_queue_size", "1024" };
|
||||
if (Encode(args, ffmpegExe, ffArgs.ToList()))
|
||||
{
|
||||
args.Logger?.ILog($"Adding {chapter} chapters to file");
|
||||
return 1;
|
||||
}
|
||||
args.Logger?.ELog("Processing failed");
|
||||
return -1;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,4 +61,5 @@ scriban
|
||||
github
|
||||
normalization
|
||||
doesn''t
|
||||
*
|
||||
*
|
||||
%
|
||||
Reference in New Issue
Block a user