diff --git a/ConsoleWhisper/ConsoleWhisper.csproj b/ConsoleWhisper/ConsoleWhisper.csproj
index 59db273..059a834 100644
--- a/ConsoleWhisper/ConsoleWhisper.csproj
+++ b/ConsoleWhisper/ConsoleWhisper.csproj
@@ -11,7 +11,7 @@
ConsoleWhisper
- 1.0.1
+ 1.1.0
azhuge233
diff --git a/ConsoleWhisper/Model/Argument.cs b/ConsoleWhisper/Model/Argument.cs
index d6b48c2..976ab56 100644
--- a/ConsoleWhisper/Model/Argument.cs
+++ b/ConsoleWhisper/Model/Argument.cs
@@ -4,8 +4,8 @@
namespace ConsoleWhisper.Model {
public class Argument {
- [Option('i', "input", Required = true, Hidden = false, Separator = ' ', HelpText = "Input media files.")]
- [Value(4)]
+ [Option('i', "input", Required = true, Hidden = false, Separator = ',', HelpText = "Input media files.")]
+ [Value(5)]
public IEnumerable Files { get; set; }
[Option('m', "model", Required = false, Hidden = false, Default = "small", HelpText = "Whisper model: base, tiny, small, medium, large.")]
@@ -24,6 +24,10 @@ public class Argument {
[Value(3, Required = false)]
public bool OnlyExtract { get; set; }
+ [Option("multithread", Required = false, Hidden = false, Default = false, HelpText = "Use multithreading when extracting soundtrack.")]
+ [Value(4, Required = false)]
+ public bool Multithread { get; set; }
+
//[Option('g', "gpu", Required = false, Hidden = true, HelpText = "Currently not implemented.")]
//[Value(2, Required = false, Default = false)]
//public bool GPU { get; set; }
@@ -39,7 +43,7 @@ public class Argument {
throw new ArgumentException(message: $"Language \"{Language}\" is not supported.\nCheck {LanguageLink} for available languages.");
}
- internal const int SupportedArgumentsCount = 7;
+ internal const int SupportedArgumentsCount = 8;
private static readonly HashSet SupportedModels = new() { "base", "tiny", "small", "medium", "large" };
private static readonly HashSet SupportedLanguages = new() { "en", "zh", "de", "es", "ru", "ko", "fr", "ja", "pt", "tr",
diff --git a/ConsoleWhisper/Module/AudioHelper.cs b/ConsoleWhisper/Module/AudioHelper.cs
index c4e6bec..27f0b52 100644
--- a/ConsoleWhisper/Module/AudioHelper.cs
+++ b/ConsoleWhisper/Module/AudioHelper.cs
@@ -1,4 +1,5 @@
-using NAudio.Wave;
+using ConsoleWhisper.Model;
+using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.IO;
@@ -8,20 +9,20 @@
namespace ConsoleWhisper.Module {
public static class AudioHelper {
- public static async Task Extract(string outputDir, string mediaFilename, bool isOnlyExtract = false) {
+ public static async Task Extract(string mediaFilename, Argument arg) {
try {
- var audioFilename = isOnlyExtract ? FileHelper.GetAudioPath(outputDir, mediaFilename) : FileHelper.GetTempMp3File();
+ var audioFilename = arg.OnlyExtract ? FileHelper.GetAudioPath(arg.OutputDir, mediaFilename) : FileHelper.GetTempMp3File();
var mediaInfo = await FFmpeg.GetMediaInfo(mediaFilename);
- int audioStreamIndex = GetAudioStreamIndex(mediaInfo.AudioStreams.ToList());
+ int audioStreamIndex = arg.Multithread ? 0 : GetAudioStreamIndex(mediaInfo.AudioStreams.ToList());
var audioStream = mediaInfo.AudioStreams
.Skip(audioStreamIndex)
.FirstOrDefault();
await DoConversion(audioStream, audioFilename);
- if (!isOnlyExtract) {
+ if (!arg.OnlyExtract) {
var resampledWaveFilename = Resample(audioFilename);
FileHelper.DelFile(audioFilename);
diff --git a/ConsoleWhisper/Module/FileHelper.cs b/ConsoleWhisper/Module/FileHelper.cs
index 7fb17e7..764994a 100644
--- a/ConsoleWhisper/Module/FileHelper.cs
+++ b/ConsoleWhisper/Module/FileHelper.cs
@@ -1,15 +1,20 @@
-using System;
+using ConsoleWhisper.Model;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
+using Xabe.FFmpeg;
+using Xabe.FFmpeg.Downloader;
namespace ConsoleWhisper.Module {
internal static class FileHelper {
+ #region Directories
internal static readonly string AppDirectory = AppDomain.CurrentDomain.BaseDirectory;
internal static readonly string FFmpegLocation = Path.Combine(AppDirectory, "ffmpeg.exe");
internal static readonly string FFprobeLocation = Path.Combine(AppDirectory, "ffprobe.exe");
internal static readonly string ModelDirectory = Path.Combine(AppDirectory, "Model");
+ #endregion
internal static IEnumerable ExpandFilePaths(IEnumerable paths) {
try {
@@ -52,15 +57,20 @@ internal static class FileHelper {
return Path.Combine(outputDir, audioName);
}
- #region Check if necessary file exists
- internal static bool ModelExists(string modelFilename) {
- return File.Exists(Path.Combine(ModelDirectory, modelFilename));
- }
-
- internal static bool FFmpegExists() {
- return File.Exists(FFmpegLocation) && File.Exists(FFprobeLocation);
+ internal static async Task DownloadFFmpegandModel(Argument arg) {
+ try {
+ FFmpeg.SetExecutablesPath(AppDirectory);
+
+ var tasks = new List() {
+ Task.Run(DownloadFFmpeg),
+ Task.Run(() => DownloadModel(arg.ModelType))
+ };
+
+ await Task.WhenAll(tasks);
+ } catch (Exception) {
+ throw;
+ }
}
- #endregion
#region Get temp file name
internal static string GetTempFile() {
@@ -125,5 +135,31 @@ internal static class FileHelper {
private const string Mp3Extension = "mp3";
private const string SrtExtension = "srt";
#endregion
+
+ #region downloader
+ private static async Task DownloadFFmpeg() {
+ if (!FFmpegExists()) {
+ Output.Warn($"FFmpeg not found, start downloading.");
+ await FFmpegDownloader.GetLatestVersion(FFmpegVersion.Official);
+ }
+ }
+
+ private static async Task DownloadModel(string ModelType) {
+ if (!ModelExists(ModelType)) {
+ Output.Warn($"{ModelType} is not found in Models directory, start downloading.");
+ await WhisperHelper.DownloadModel(ModelType);
+ }
+ }
+ #endregion
+
+ #region Check if necessary file exists
+ private static bool ModelExists(string modelFilename) {
+ return File.Exists(Path.Combine(ModelDirectory, modelFilename));
+ }
+
+ private static bool FFmpegExists() {
+ return File.Exists(FFmpegLocation) && File.Exists(FFprobeLocation);
+ }
+ #endregion
}
}
diff --git a/ConsoleWhisper/Module/Output.cs b/ConsoleWhisper/Module/Output.cs
index 8fdea84..e76dbf8 100644
--- a/ConsoleWhisper/Module/Output.cs
+++ b/ConsoleWhisper/Module/Output.cs
@@ -17,7 +17,12 @@ internal static class Output {
Console.WriteLine(msg);
ResetColor();
}
- internal static void Success(string msg) {
+ internal static void WarnR(string msg) {
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.Write(msg);
+ ResetColor();
+ }
+ internal static void Success(string msg) {
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(msg);
ResetColor();
diff --git a/ConsoleWhisper/Runner.cs b/ConsoleWhisper/Runner.cs
index 7fc500b..0d5b195 100644
--- a/ConsoleWhisper/Runner.cs
+++ b/ConsoleWhisper/Runner.cs
@@ -3,11 +3,9 @@
using ConsoleWhisper.Model;
using ConsoleWhisper.Module;
using System.Collections.Generic;
-using System.IO;
using System.Threading.Tasks;
using System;
-using Xabe.FFmpeg.Downloader;
-using Xabe.FFmpeg;
+using System.Linq;
namespace ConsoleWhisper {
internal static class Runner {
@@ -16,33 +14,15 @@ internal static class Runner {
arg.Validate();
arg.ModelType = FileHelper.GetModelName(arg.ModelType);
- arg.Files = FileHelper.ExpandFilePaths(arg.Files);
+ var mediaFileList = FileHelper.ExpandFilePaths(arg.Files).ToList();
- FFmpeg.SetExecutablesPath(FileHelper.AppDirectory);
+ await FileHelper.DownloadFFmpegandModel(arg);
- if (!FileHelper.FFmpegExists()) {
- Output.Warn($"FFmpeg not found, start downloading.");
- await FFmpegDownloader.GetLatestVersion(FFmpegVersion.Official);
+ if (arg.Multithread) {
+ await DoMultithread(mediaFileList, arg);
+ } else {
+ await DoSinglethread(mediaFileList, arg);
}
-
- if (!FileHelper.ModelExists(arg.ModelType)) {
- Output.Warn($"{arg.ModelType} is not found in Models directory, start downloading.");
- await WhisperHelper.DownloadModel(arg.ModelType);
- }
-
- int cnt = 1;
- foreach (var file in arg.Files) {
- var mediaFilename = Path.GetFileName(file);
- var wavFilename = await AudioHelper.Extract(arg.OutputDir, file, arg.OnlyExtract);
-
- if (!arg.OnlyExtract) {
- Output.Info($"Start transcribing file #{cnt++}: {mediaFilename}");
- await WhisperHelper.Transcribe(arg.ModelType, wavFilename, mediaFilename, arg.OutputDir, arg.Language);
- FileHelper.DelFile(wavFilename);
- }
- }
-
-
} catch (Exception) {
throw;
}
@@ -59,5 +39,86 @@ internal static class Runner {
Output.Help(helpText, errs.IsHelp(), errs.IsVersion());
}
+
+ private static async Task DoMultithread(List mediaFileList, Argument arg) {
+ try {
+ Output.WarnR($"Use multithreading will disable user input.\n" +
+ $"If media file has multiple soundtracks, program will extract the first one, you will NOT be able to choose.\n" +
+ $"If you are transcribing, make sure to have enough disk space to store the temp .wav file.\n" +
+ $"Proceed? yes(y)/No(N)): ");
+
+ var confirm = Console.ReadLine().TrimEnd(Environment.NewLine.ToCharArray());
+ if (confirm.ToLower() != "y" && confirm.ToLower() != "yes") {
+ Output.Error($"Aborted.");
+ return;
+ }
+
+ var tasks = Enumerable.Range(0, mediaFileList.Count)
+ .Select(i => Task.Run(() => DoExtractMultithread(mediaFileList[i], arg)));
+
+ var mediaWavMap = await Task.WhenAll(tasks);
+
+ if (!arg.OnlyExtract) {
+ await DoTranscribeMultithread(mediaWavMap, arg);
+ }
+ } catch (Exception) {
+ throw;
+ }
+ }
+
+ private static async Task DoSinglethread(List mediaFileList, Argument arg) {
+ try {
+ int cnt = 1;
+ foreach (var mediaFilename in mediaFileList) {
+ var wavFilename = await DoExtract(mediaFilename, arg);
+ if (!arg.OnlyExtract) {
+ await DoTranscribe(wavFilename, mediaFilename, cnt++, arg);
+ }
+ }
+ } catch (Exception) {
+ throw;
+ }
+ }
+
+ private static async Task DoExtract(string mediaFilename, Argument arg) {
+ try {
+ var wavFilename = await AudioHelper.Extract(mediaFilename, arg);
+ return wavFilename;
+ } catch (Exception) {
+ throw;
+ }
+ }
+
+ private static async Task> DoExtractMultithread(string mediaFilename, Argument arg) {
+ try {
+ var wavFilename = await AudioHelper.Extract(mediaFilename, arg);
+ return new KeyValuePair(mediaFilename, wavFilename);
+ } catch (Exception ) {
+ throw;
+ }
+ }
+
+ private static async Task DoTranscribe(string wavFilename, string mediaFilename, int index, Argument arg) {
+ try {
+ await WhisperHelper.Transcribe(arg.ModelType, wavFilename, mediaFilename, arg.OutputDir, arg.Language);
+ Output.Info($"Start transcribing media #{index}: {mediaFilename}");
+ FileHelper.DelFile(wavFilename);
+ } catch (Exception) {
+ throw;
+ }
+ }
+
+ private static async Task DoTranscribeMultithread(KeyValuePair[] mediaWavMap, Argument arg) {
+ try {
+ int cnt = 1;
+ foreach (var pair in mediaWavMap) {
+ var wavFilename = pair.Value;
+ var mediaFilename = pair.Key;
+ await DoTranscribe(wavFilename, mediaFilename, cnt++, arg);
+ }
+ } catch (Exception) {
+ throw;
+ }
+ }
}
}