Skip to content

Commit

Permalink
1.1.0
Browse files Browse the repository at this point in the history
- Add audio parallel ectracting.
- Add model/ffmpeg parallel downloading.
  • Loading branch information
azhuge233 committed Jun 22, 2023
1 parent 3107771 commit f9fbc34
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 46 deletions.
2 changes: 1 addition & 1 deletion ConsoleWhisper/ConsoleWhisper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<PropertyGroup>
<AssemblyName>ConsoleWhisper</AssemblyName>
<Version>1.0.1</Version>
<Version>1.1.0</Version>
<Authors>azhuge233</Authors>
</PropertyGroup>

Expand Down
10 changes: 7 additions & 3 deletions ConsoleWhisper/Model/Argument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> Files { get; set; }

[Option('m', "model", Required = false, Hidden = false, Default = "small", HelpText = "Whisper model: base, tiny, small, medium, large.")]
Expand All @@ -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; }
Expand All @@ -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<string> SupportedModels = new() { "base", "tiny", "small", "medium", "large" };
private static readonly HashSet<string> SupportedLanguages = new() { "en", "zh", "de", "es", "ru", "ko", "fr", "ja", "pt", "tr",
Expand Down
11 changes: 6 additions & 5 deletions ConsoleWhisper/Module/AudioHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NAudio.Wave;
using ConsoleWhisper.Model;
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.IO;
Expand All @@ -8,20 +9,20 @@

namespace ConsoleWhisper.Module {
public static class AudioHelper {
public static async Task<string> Extract(string outputDir, string mediaFilename, bool isOnlyExtract = false) {
public static async Task<string> 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);

Expand Down
54 changes: 45 additions & 9 deletions ConsoleWhisper/Module/FileHelper.cs
Original file line number Diff line number Diff line change
@@ -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<string> ExpandFilePaths(IEnumerable<string> paths) {
try {
Expand Down Expand Up @@ -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>() {
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() {
Expand Down Expand Up @@ -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
}
}
7 changes: 6 additions & 1 deletion ConsoleWhisper/Module/Output.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
115 changes: 88 additions & 27 deletions ConsoleWhisper/Runner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
}
Expand All @@ -59,5 +39,86 @@ internal static class Runner {

Output.Help(helpText, errs.IsHelp(), errs.IsVersion());
}

private static async Task DoMultithread(List<string> 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<string> 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<string> DoExtract(string mediaFilename, Argument arg) {
try {
var wavFilename = await AudioHelper.Extract(mediaFilename, arg);
return wavFilename;
} catch (Exception) {
throw;
}
}

private static async Task<KeyValuePair<string, string>> DoExtractMultithread(string mediaFilename, Argument arg) {
try {
var wavFilename = await AudioHelper.Extract(mediaFilename, arg);
return new KeyValuePair<string, string>(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<string, string>[] 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;
}
}
}
}

0 comments on commit f9fbc34

Please sign in to comment.