diff --git a/.gitignore b/.gitignore index f219822..01ded0f 100644 --- a/.gitignore +++ b/.gitignore @@ -376,4 +376,6 @@ FodyWeavers.xsd # Local History for Visual Studio Code .history/ -tmp/ \ No newline at end of file +tmp/ +uploadcache/* +spoken/* diff --git a/Config.cs b/Config.cs new file mode 100644 index 0000000..e331381 --- /dev/null +++ b/Config.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; + +public class Config +{ + public string speech_service { get; set; } = "http://eligos.lan:5400"; + public string sync_dropoff { get; set; } = "/home/adam/Sync/eligos documents/"; +} diff --git a/Program.cs b/Program.cs index eefbff7..98682de 100644 --- a/Program.cs +++ b/Program.cs @@ -1,24 +1,185 @@ -using System; -using franz; - -namespace PlaceholderVO -{ - internal class Program - { - public static Telefranz telefranz; - - static async Task Main(string[] args) - { - Telefranz.Configure(name: "balaam placeholder VO", bootstrap_servers: "alloces:9092", - commands: ["speak"]); - telefranz = Telefranz.Instance; - - await Task.Delay(1000); - telefranz.ProduceMessage(new gray_messages.global.sound_off()); - Console.WriteLine("Hello World!"); - - await Task.Delay(-1); - - } - } -} +using System.Collections.Concurrent; +using System.Data; +using System.Diagnostics; +using System.Net; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Timers; +using Newtonsoft.Json; + + +namespace placeholdervo +{ + public partial class Program + { + + [GeneratedRegex("^[^a-zA-Z0-9'\"]")] + private static partial Regex TextLine(); + public static Config conf; + private static string workingDir = "./"; + + private static ConcurrentQueue scriptFragmentFiles = new ConcurrentQueue(); + private static CancellationToken scriptFileCancellationToken; + + private static async void webcall() + { + var hc = new HttpClient(){ BaseAddress = new Uri(conf.speech_service)}; + while(true) + { + while(!scriptFragmentFiles.IsEmpty) + { + string scriptFragment; + scriptFragmentFiles.TryDequeue(out scriptFragment); + if(scriptFragment == null) continue; + Console.WriteLine(scriptFragment); + + HttpContent fileStreamContent = new StreamContent(File.OpenRead(scriptFragment)); + using (var formData = new MultipartFormDataContent()) + { + formData.Add(fileStreamContent, "file1", "file1"); + + var response = await hc.PostAsync("/speak", formData); + if (!response.IsSuccessStatusCode) + { + Console.Error.WriteLine($"{response.StatusCode} - {response.ReasonPhrase} - {await response.Content.ReadAsStringAsync()}"); + } + else + { + var resp = string.Join(", ", JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync())); + Console.WriteLine($"success; {resp}."); + if(resp != null) + { + scriptFragmentVoiceFiles[resp] = scriptFragment; + Console.WriteLine("Ditching script fragment file."); + File.Delete(scriptFragment); + } + } + } + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1)); + } + + if(scriptFileCancellationToken.IsCancellationRequested && scriptFragmentFiles.IsEmpty) + { + return; + } + } + } + + private static ConcurrentDictionary scriptFragmentVoiceFiles = new ConcurrentDictionary(); + + private static async void filePickup() + { + while(true) + { + while(!scriptFragmentVoiceFiles.IsEmpty) + { + var files = Directory.GetFiles(conf.sync_dropoff); + Console.WriteLine($"files listed: {string.Join(", ", files)}"); + Console.WriteLine($"hoping for: {string.Join(", ", scriptFragmentVoiceFiles.Keys)}"); + // Console.WriteLine($"sanity check: first listed file: {Path.GetFileName(files.FirstOrDefault())}"); + // Console.WriteLine($"sanity check: first key: {scriptFragmentVoiceFiles.Keys.FirstOrDefault()}"); + // Console.WriteLine($"sanity check: first value: {scriptFragmentVoiceFiles[scriptFragmentVoiceFiles.Keys.FirstOrDefault()]}"); + var audioFile = files.FirstOrDefault(f => scriptFragmentVoiceFiles.ContainsKey(Path.GetFileName(f))); + if(!string.IsNullOrWhiteSpace(audioFile)) + { + Console.WriteLine($"found {audioFile}"); + try + { + var bareAudioFile = Path.GetFileName(audioFile); + var destFile = scriptFragmentVoiceFiles[bareAudioFile]; + destFile = Path.GetFileNameWithoutExtension(destFile) + ".wav"; + destFile = Path.Combine(workingDir, destFile); + File.Move(audioFile, destFile); + + if(!scriptFragmentVoiceFiles.Remove(bareAudioFile, out string throwaway)) + { + Console.WriteLine($"failed to remove {bareAudioFile}"); + } + } + catch(Exception e) + { + Console.Error.WriteLine(e.Message); + } + } + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5)); + } + if(scriptFileCancellationToken.IsCancellationRequested) + { + return; + } + + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1)); + } + } + + public static async Task Main(string[] args) + { + conf = JsonConvert.DeserializeObject( + File.ReadAllText( + AppDomain.CurrentDomain.BaseDirectory + "appsettings.json")) + ?? new Config(); + + var scriptPath = args?.FirstOrDefault(); + if(string.IsNullOrWhiteSpace(scriptPath)) + scriptPath = "./script.txt"; + workingDir = Path.GetDirectoryName(scriptPath); + + var cancelSource = new CancellationTokenSource(); + scriptFileCancellationToken = cancelSource.Token; + var apiCallThread = new Thread(new ThreadStart(webcall)); + apiCallThread.Start(); + var fileRetrieveThread = new Thread(new ThreadStart(filePickup)); + fileRetrieveThread.Start(); + + var lines = File.ReadAllLines(scriptPath).Where(l => !TextLine().IsMatch(l)) + .Append("\n").Append("\n"); //pad out file so my tactic for separating doesn't fall off the end of the file + var lineCountMagnitude = (int)Math.Log10(lines.Count()); + + var runningSavedLines = new List(); + var completeSavedLines = new List(); + var iteration = 0; + + foreach(var l in lines) + { + if(string.IsNullOrWhiteSpace(l)) + { + if(runningSavedLines.Any(line => !string.IsNullOrWhiteSpace(line))) + { + ++iteration; + //x.ToString("D2") should format x with minimum 2 digits, padded left + var scriptFragment = $"script{iteration.ToString($"D{lineCountMagnitude}")}.txt"; + File.WriteAllLines(scriptFragment, runningSavedLines); + scriptFragmentFiles.Enqueue(scriptFragment); + completeSavedLines.AddRange(runningSavedLines); + } + runningSavedLines = []; + } + else + { + runningSavedLines.Add(l); + } + } + + var scriptStripped = $"script{0.ToString($"D{lineCountMagnitude}")}.txt"; + File.WriteAllLines(scriptStripped, completeSavedLines); + scriptFragmentFiles.Enqueue(scriptStripped); + cancelSource.Cancel(); + Console.WriteLine("http client \"cancelled\"."); + while(apiCallThread.ThreadState != System.Threading.ThreadState.Stopped) + { + Console.WriteLine("thread still running. waiting."); + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5)); + } + Console.WriteLine($"api call thread done ({apiCallThread.ThreadState})."); + + while(fileRetrieveThread.ThreadState != System.Threading.ThreadState.Stopped) + { + Console.WriteLine("fileRetrieveThread still running. waiting."); + System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5)); + } + Console.WriteLine($"fileRetrieveThread done ({fileRetrieveThread.ThreadState}). kbai"); + } + } +} diff --git a/appsettings.json b/appsettings.json new file mode 100644 index 0000000..6f2cc32 --- /dev/null +++ b/appsettings.json @@ -0,0 +1,4 @@ +{ + "speech_service": "http://eligos.lan:5400", + "sync_dropoff": "/home/adam/Sync/eligos documents/" +} \ No newline at end of file diff --git a/placeholder-VO.csproj b/placeholder-VO.csproj index ca21fc4..51ee873 100644 --- a/placeholder-VO.csproj +++ b/placeholder-VO.csproj @@ -5,11 +5,11 @@ net8.0 placeholder_VO enable - disable + enable - - + +