mirror of
https://github.com/SubtitleEdit/subtitleedit.git
synced 2024-11-21 18:52:36 +01:00
Merge pull request #8281 from ivandrofly/feature/generatevideowithhardsubs
Feature/generatevideowithhardsubs
This commit is contained in:
commit
ca9fa80b4b
34
src/Test/Logic/DimensionTest.cs
Normal file
34
src/Test/Logic/DimensionTest.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -76,6 +76,7 @@
|
||||
<Compile Include="Logic\BeautifyTimeCodesTest.cs" />
|
||||
<Compile Include="Logic\ConvertColorsToDialogTest.cs" />
|
||||
<Compile Include="Core\LanguageAutoDetectLanguagesTest.cs" />
|
||||
<Compile Include="Logic\DimensionTest.cs" />
|
||||
<Compile Include="Logic\NetflixHelperTest.cs" />
|
||||
<Compile Include="Logic\SubtitleFormats\EbuStlTest.cs" />
|
||||
<Compile Include="Logic\SubtitleFormats\NetflixTimedTextTest.cs" />
|
||||
|
40
src/libse/Common/Dimension.cs
Normal file
40
src/libse/Common/Dimension.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,8 +11,8 @@ namespace Nikse.SubtitleEdit.Core.Common
|
||||
public class FfmpegMediaInfo
|
||||
{
|
||||
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);
|
||||
|
||||
@ -65,13 +65,12 @@ namespace Nikse.SubtitleEdit.Core.Common
|
||||
if (resolutionMatch.Success)
|
||||
{
|
||||
var parts = resolutionMatch.Value.Split('x');
|
||||
if (info.VideoWidth == 0 &&
|
||||
if (info.Dimension.Width == 0 &&
|
||||
parts.Length == 2 &&
|
||||
int.TryParse(parts[0], out var w) &&
|
||||
int.TryParse(parts[1], out var h))
|
||||
{
|
||||
info.VideoWidth = w;
|
||||
info.VideoHeight = h;
|
||||
info.Dimension = new Dimension(h, w);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ using Nikse.SubtitleEdit.Core.VobSub;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
@ -707,5 +708,53 @@ namespace Nikse.SubtitleEdit.Core.Common
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(fileName).ToLowerInvariant();
|
||||
if ((Utilities.AudioFileExtensions.Contains(ext) || Utilities.VideoFileExtensions.Contains(ext)) && File.Exists(fileName))
|
||||
var ext = Path.GetExtension(videoFileName).ToLowerInvariant();
|
||||
if ((Utilities.AudioFileExtensions.Contains(ext) || Utilities.VideoFileExtensions.Contains(ext)) && File.Exists(videoFileName))
|
||||
{
|
||||
var item = new BatchVideoAndSub();
|
||||
item.VideoFileName = fileName;
|
||||
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))
|
||||
var videoDimension = GetVideoDimension(videoFileName);
|
||||
if (!videoDimension.IsValid())
|
||||
{
|
||||
subFileName = Path.ChangeExtension(fileName, ".srt");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(subFileName))
|
||||
{
|
||||
var files = Directory.GetFiles(path, fileNameNoExt + "*.ass");
|
||||
if (files.Length > 0)
|
||||
{
|
||||
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);
|
||||
|
||||
var batchVideoAndSub = CreateBatchVideoAndSub(videoFileName);
|
||||
var listViewItem = new ListViewItem(videoFileName) { Tag = batchVideoAndSub };
|
||||
listViewItem.SubItems.Add(videoDimension.ToString());
|
||||
var s = Utilities.FormatBytesToDisplayFileSize(batchVideoAndSub.VideoFileSizeInBytes);
|
||||
listViewItem.SubItems.Add(s);
|
||||
listViewItem.SubItems.Add(Path.GetFileName(item.SubtitleFileName));
|
||||
listViewItem.SubItems.Add(Path.GetFileName(batchVideoAndSub.SubtitleFileName));
|
||||
listViewItem.SubItems.Add(string.Empty);
|
||||
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)
|
||||
{
|
||||
var indices = new List<int>();
|
||||
|
Loading…
Reference in New Issue
Block a user