Merge pull request #8281 from ivandrofly/feature/generatevideowithhardsubs

Feature/generatevideowithhardsubs
This commit is contained in:
Nikolaj Olsson 2024-04-30 18:35:16 +02:00 committed by GitHub
commit ca9fa80b4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 196 additions and 85 deletions

View File

@ -0,0 +1,34 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Nikse.SubtitleEdit.Core.Common;
namespace Test.Logic
{
[TestClass]
public class DimensionTest
{
[TestMethod]
public void InvalidTest()
{
var dimension = new Dimension();
Assert.AreEqual(0, dimension.Width);
Assert.AreEqual(0, dimension.Height);
Assert.IsTrue(!dimension.IsValid());
}
[TestMethod]
public void ValidTest()
{
var dimension = new Dimension(10, 10);
Assert.IsTrue(dimension.IsValid());
}
[TestMethod]
public void EqualityTest()
{
var dimensionOne = new Dimension();
var dimensionTwo = new Dimension();
Assert.AreEqual(dimensionOne, dimensionTwo);
Assert.IsTrue(dimensionOne == dimensionTwo);
}
}
}

View File

@ -76,6 +76,7 @@
<Compile Include="Logic\BeautifyTimeCodesTest.cs" /> <Compile Include="Logic\BeautifyTimeCodesTest.cs" />
<Compile Include="Logic\ConvertColorsToDialogTest.cs" /> <Compile Include="Logic\ConvertColorsToDialogTest.cs" />
<Compile Include="Core\LanguageAutoDetectLanguagesTest.cs" /> <Compile Include="Core\LanguageAutoDetectLanguagesTest.cs" />
<Compile Include="Logic\DimensionTest.cs" />
<Compile Include="Logic\NetflixHelperTest.cs" /> <Compile Include="Logic\NetflixHelperTest.cs" />
<Compile Include="Logic\SubtitleFormats\EbuStlTest.cs" /> <Compile Include="Logic\SubtitleFormats\EbuStlTest.cs" />
<Compile Include="Logic\SubtitleFormats\NetflixTimedTextTest.cs" /> <Compile Include="Logic\SubtitleFormats\NetflixTimedTextTest.cs" />

View File

@ -0,0 +1,40 @@
using System;
namespace Nikse.SubtitleEdit.Core.Common
{
/// <summary>
/// Struct representing the dimensions of an object.
/// </summary>
public struct Dimension : IEquatable<Dimension>
{
public int Height { get; set; }
public int Width { get; set; }
public Dimension(int height, int width) => (Height, Width) = (height, width);
/// <summary>
/// Returns a string representation of the Dimension object, representing its height and width.
/// </summary>
/// <returns>A string representation of the Dimension object, in the format "height x width".</returns>
public override string ToString() => $"{Height}x{Width}";
public bool Equals(Dimension other) => Height == other.Height && Width == other.Width;
public override bool Equals(object obj) => obj is Dimension other && Equals(other);
public static bool operator ==(Dimension left, Dimension right) => left.Equals(right);
public static bool operator !=(Dimension left, Dimension right) => !(left == right);
/// <summary>
/// Checks if the dimension (height and width) is valid.
/// </summary>
/// <returns>True if the dimension is valid; otherwise, false.</returns>
public bool IsValid() => Width > 0 && Height > 0;
public override int GetHashCode()
{
unchecked
{
return (Height * 397) ^ Width;
}
}
}
}

View File

@ -11,8 +11,8 @@ namespace Nikse.SubtitleEdit.Core.Common
public class FfmpegMediaInfo public class FfmpegMediaInfo
{ {
public List<FfmpegTrackInfo> Tracks { get; set; } public List<FfmpegTrackInfo> Tracks { get; set; }
public int VideoWidth { get; set; }
public int VideoHeight { get; set; } public Dimension Dimension { get; set; }
private static readonly Regex ResolutionRegex = new Regex(@"\d\d+x\d\d+", RegexOptions.Compiled); private static readonly Regex ResolutionRegex = new Regex(@"\d\d+x\d\d+", RegexOptions.Compiled);
@ -65,13 +65,12 @@ namespace Nikse.SubtitleEdit.Core.Common
if (resolutionMatch.Success) if (resolutionMatch.Success)
{ {
var parts = resolutionMatch.Value.Split('x'); var parts = resolutionMatch.Value.Split('x');
if (info.VideoWidth == 0 && if (info.Dimension.Width == 0 &&
parts.Length == 2 && parts.Length == 2 &&
int.TryParse(parts[0], out var w) && int.TryParse(parts[0], out var w) &&
int.TryParse(parts[1], out var h)) int.TryParse(parts[1], out var h))
{ {
info.VideoWidth = w; info.Dimension = new Dimension(h, w);
info.VideoHeight = h;
} }
} }

View File

@ -6,6 +6,7 @@ using Nikse.SubtitleEdit.Core.VobSub;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -707,5 +708,53 @@ namespace Nikse.SubtitleEdit.Core.Common
return false; return false;
} }
public static string TryLocateSubtitleFile(string path, string videoFileName)
{
// search in these subdirectories: \Subs;\Sub;\Subtitles;
var knownSubtitleDirectories = new[]
{
path, Path.Combine(path, "Subs"), Path.Combine(path, "Sub"), Path.Combine(path, "Subtitles")
};
// handles if video file was sent with full path
if (Path.IsPathRooted(videoFileName))
{
videoFileName = Path.GetFileName(videoFileName);
}
foreach (var knownSubtitleDirectory in knownSubtitleDirectories)
{
if (!Directory.Exists(knownSubtitleDirectory))
{
continue;
}
// try to locate subtitle file that has the same name as the video file
var defaultSubtitles = new[]
{
Path.Combine(knownSubtitleDirectory, Path.ChangeExtension(videoFileName, ".ass")),
Path.Combine(knownSubtitleDirectory, Path.ChangeExtension(videoFileName, ".srt"))
};
foreach (var defaultSubtitle in defaultSubtitles)
{
if (File.Exists(defaultSubtitle))
{
return defaultSubtitle;
}
}
// get first subtitle in path with extension .ass or .srt
var assEnumerable = Directory.EnumerateFiles(knownSubtitleDirectory, "*.ass", SearchOption.TopDirectoryOnly);
var subRipEnumerable = Directory.EnumerateFiles(knownSubtitleDirectory, "*.srt", SearchOption.TopDirectoryOnly);
var subtitleFile = assEnumerable.Concat(subRipEnumerable).FirstOrDefault();
if (!string.IsNullOrEmpty(subtitleFile))
{
return subtitleFile;
}
}
return string.Empty;
}
} }
} }

View File

@ -2139,100 +2139,88 @@ namespace Nikse.SubtitleEdit.Forms
} }
} }
private void AddInputFile(string fileName) private void AddInputFile(string videoFileName)
{ {
if (string.IsNullOrEmpty(fileName)) if (string.IsNullOrEmpty(videoFileName))
{ {
return; return;
} }
var ext = Path.GetExtension(fileName).ToLowerInvariant(); var ext = Path.GetExtension(videoFileName).ToLowerInvariant();
if ((Utilities.AudioFileExtensions.Contains(ext) || Utilities.VideoFileExtensions.Contains(ext)) && File.Exists(fileName)) if ((Utilities.AudioFileExtensions.Contains(ext) || Utilities.VideoFileExtensions.Contains(ext)) && File.Exists(videoFileName))
{ {
var item = new BatchVideoAndSub(); var videoDimension = GetVideoDimension(videoFileName);
item.VideoFileName = fileName; if (!videoDimension.IsValid())
item.VideoFileSizeInBytes = new FileInfo(fileName).Length;
var path = Path.GetDirectoryName(fileName);
var fileNameNoExt = Path.GetFileNameWithoutExtension(fileName);
var subFileName = Path.ChangeExtension(fileName, ".ass");
if (!File.Exists(subFileName))
{ {
subFileName = Path.ChangeExtension(fileName, ".srt"); return;
} }
if (!File.Exists(subFileName)) var batchVideoAndSub = CreateBatchVideoAndSub(videoFileName);
{ var listViewItem = new ListViewItem(videoFileName) { Tag = batchVideoAndSub };
var files = Directory.GetFiles(path, fileNameNoExt + "*.ass"); listViewItem.SubItems.Add(videoDimension.ToString());
if (files.Length > 0) var s = Utilities.FormatBytesToDisplayFileSize(batchVideoAndSub.VideoFileSizeInBytes);
{
subFileName = files[0];
}
}
if (!File.Exists(subFileName))
{
var files = Directory.GetFiles(path, fileNameNoExt + "*.srt");
if (files.Length > 0)
{
subFileName = files[0];
}
}
if (File.Exists(subFileName))
{
item.SubtitleFileName = subFileName;
item.SubtitleFileFileSizeInBytes = new FileInfo(subFileName).Length;
}
var mediaInfo = FfmpegMediaInfo.Parse(fileName);
int width;
int height;
if (mediaInfo.VideoWidth > 0 && mediaInfo.VideoHeight > 0)
{
width = mediaInfo.VideoWidth;
height = mediaInfo.VideoHeight;
}
else
{
var vInfo = new VideoInfo { Success = false };
if (fileName.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase))
{
vInfo = QuartsPlayer.GetVideoInfo(fileName);
if (!vInfo.Success)
{
vInfo = LibMpvDynamic.GetVideoInfo(fileName);
}
}
if (!vInfo.Success)
{
vInfo = UiUtil.GetVideoInfo(fileName);
}
width = vInfo.Width;
height = vInfo.Height;
}
if (width == 0 || height == 0)
{
SeLogger.Error("Skipping burn-in file with no video: " + fileName);
return; // skip audio or damaged files
}
var listViewItem = new ListViewItem(fileName);
listViewItem.Tag = item;
listViewItem.SubItems.Add($"{width}x{height}");
var s = Utilities.FormatBytesToDisplayFileSize(item.VideoFileSizeInBytes);
listViewItem.SubItems.Add(s); listViewItem.SubItems.Add(s);
listViewItem.SubItems.Add(Path.GetFileName(item.SubtitleFileName)); listViewItem.SubItems.Add(Path.GetFileName(batchVideoAndSub.SubtitleFileName));
listViewItem.SubItems.Add(string.Empty); listViewItem.SubItems.Add(string.Empty);
listViewBatch.Items.Add(listViewItem); listViewBatch.Items.Add(listViewItem);
_batchVideoAndSubList.Add(item); _batchVideoAndSubList.Add(batchVideoAndSub);
} }
} }
private BatchVideoAndSub CreateBatchVideoAndSub(string videoFileName)
{
var batchVideoAndSub = new BatchVideoAndSub
{
VideoFileName = videoFileName,
VideoFileSizeInBytes = new FileInfo(videoFileName).Length
};
var path = Path.GetDirectoryName(videoFileName);
// try to locate subtitle file for the input vide file
var subtitleFile = FileUtil.TryLocateSubtitleFile(path, videoFileName);
if (File.Exists(subtitleFile))
{
batchVideoAndSub.SubtitleFileName = subtitleFile;
batchVideoAndSub.SubtitleFileFileSizeInBytes = new FileInfo(subtitleFile).Length;
}
return batchVideoAndSub;
}
private Dimension GetVideoDimension(string videoFileName)
{
var mediaInfo = FfmpegMediaInfo.Parse(videoFileName);
if (mediaInfo.Dimension.IsValid())
{
return mediaInfo.Dimension;
}
var vInfo = new VideoInfo { Success = false };
if (videoFileName.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase))
{
vInfo = QuartsPlayer.GetVideoInfo(videoFileName);
if (!vInfo.Success)
{
vInfo = LibMpvDynamic.GetVideoInfo(videoFileName);
}
}
if (!vInfo.Success)
{
vInfo = UiUtil.GetVideoInfo(videoFileName);
}
var dimension = new Dimension(vInfo.Height, vInfo.Width);
// skip audio or damaged files
if (!dimension.IsValid())
{
SeLogger.Error("Skipping burn-in file with no video: " + videoFileName);
}
return dimension;
}
private void deleteToolStripMenuItem_Click(object sender, EventArgs e) private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
{ {
var indices = new List<int>(); var indices = new List<int>();