script-splitter/Program.cs

265 lines
11 KiB
C#
Raw Normal View History

using System.Collections.Concurrent;
using System.Data;
using System.Diagnostics;
using System.Net;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using System.Xml.Schema;
using Newtonsoft.Json;
namespace placeholdervo
{
public partial class Program
{
[GeneratedRegex("^(\\[|#)")]
private static partial Regex NonTextLine();
public static Config conf;
private static string workingDir = "./";
2024-09-20 21:23:08 -04:00
private static HttpClient hc;
private static ConcurrentQueue<string> titleCardLines = new ConcurrentQueue<string>();
private static ConcurrentQueue<string> noteLines = new ConcurrentQueue<string>();
2024-09-22 17:46:17 -04:00
private static ConcurrentDictionary<string, string> filePickups = new ConcurrentDictionary<string, string>();
private static CancellationToken scriptFileCancellationToken;
2024-09-22 17:46:17 -04:00
///<summary>mostly just to give myself the reminder about paths</summary>
///<param name="src">the *complete path* of the awaited file. you might want conf.sync_dropoff</param>
///<param name="dest">the *complete path* to move the file to.</param>
private static async void awaitFile(string src, string dest)
{
filePickups[src] = dest;
}
private static async void filePickup()
{
while(true)
{
foreach(var srcKey in filePickups.Keys)
{
// var files = Directory.GetFiles(conf.sync_dropoff);
// Console.WriteLine($"files listed: {string.Join(", ", files)}");
// Console.WriteLine($"hoping for: {string.Join(", ", filePickups.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()]}");
if(File.Exists(srcKey))
{
Console.WriteLine($"found {srcKey}");
string dest;
if (filePickups.TryRemove(srcKey, out dest))
{
File.Move(srcKey, dest);
}
else
{
Console.Error.WriteLine($"Failed to remove {srcKey} from filepickups. Unrecoverable, I think.");
Environment.Exit(-1);
}
}
}
if(scriptFileCancellationToken.IsCancellationRequested)
{
return;
}
2024-09-22 17:46:17 -04:00
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
}
}
private static async void FFStuff()
{
var cardCount = 0;
foreach(var tcL in titleCardLines)
{
await Process.Start(conf.titlecard_command, tcL).WaitForExitAsync();
File.Move("tc.png", Path.Combine(conf.titlecards_dest, $"tc{cardCount++}.png"));
}
cardCount = 0;
foreach(var nL in noteLines)
{
await Process.Start(conf.titlecard_command, nL).WaitForExitAsync();
File.Move("n0.png", Path.Combine(conf.notes_dest, $"note{cardCount++}.png"));
}
}
public static async Task Main(string[] args)
{
conf = JsonConvert.DeserializeObject<Config>(
File.ReadAllText(
AppDomain.CurrentDomain.BaseDirectory + "appsettings.json"))
?? new Config();
2024-09-20 21:23:08 -04:00
hc = new HttpClient(){ BaseAddress = new Uri(conf.speech_service)};
var taskHeap = new List<Task>();
2024-09-20 21:23:08 -04:00
var scriptPath = args?.FirstOrDefault();
if(string.IsNullOrWhiteSpace(scriptPath))
{
scriptPath = "./script.md";
if(!File.Exists(scriptPath))
{
scriptPath = "./script.txt";
if(!File.Exists(scriptPath))
{
Console.Error.WriteLine("no script found.");
return;
}
}
}
var scriptBasename = Path.GetFileNameWithoutExtension(scriptPath);
workingDir = Path.GetDirectoryName(scriptPath);
var cancelSource = new CancellationTokenSource();
scriptFileCancellationToken = cancelSource.Token;
var lines = File.ReadAllLines(scriptPath)
.Append("\n").Append("\n");
var primaryVOLines = new List<string>();
2024-08-07 20:04:28 -04:00
var titlecardUnprocessed = new List<string>();
var titlecardLines = new List<string>();
var directiveLines = new List<string>();
2024-08-07 20:04:28 -04:00
var altVOLines = new List<string>();
var noteLines = new List<string>();
foreach(var l in lines)
{
if(l.StartsWith('#'))
{
2024-08-07 20:04:28 -04:00
titlecardUnprocessed.Add(l);
}
else if (l.StartsWith('['))
{
directiveLines.Add(l);
}
else
{
//as opposed to Environment.NewLine, which will be running on linux, and thus \n. ms speech api cooperates more with \r\n.
l.ReplaceLineEndings("\r\n");
primaryVOLines.Add(l);
}
}
var scriptStripped = $"{scriptBasename}-primaryVO.txt";
File.WriteAllLines(scriptStripped, primaryVOLines);
taskHeap.Add(ttsVO(scriptStripped, Path.Combine(conf.VODropoff, $"primaryVO")));
2024-09-20 21:23:08 -04:00
var titleLevels = new List<int>(){0};
var titleIncrementDepth = 0;
2024-08-07 20:04:28 -04:00
foreach(var l in titlecardUnprocessed)
{
titleIncrementDepth = 1;
var thisString = l;
while(thisString.StartsWith('#'))
{
thisString = thisString.Substring(1);
if(!thisString.StartsWith('#'))
{
if(titleLevels.Count < titleIncrementDepth)
{
titleLevels = [.. titleLevels, 0];
}
titleLevels[titleIncrementDepth-1]++;
if(titleLevels.Count > titleIncrementDepth)
{
titleLevels.RemoveRange(titleIncrementDepth, titleLevels.Count - titleIncrementDepth);
}
var TitleCardText = thisString.Trim();
if(titleLevels.Count > 1)
{
TitleCardText = string.Join('.', titleLevels[1..]) + ": " + TitleCardText;
}
Console.WriteLine($"title card: {TitleCardText}");
2024-08-07 20:04:28 -04:00
titlecardLines.Add(TitleCardText);
}
else
{
titleIncrementDepth++;
}
}
}
foreach(var l in directiveLines)
{
var thisLine = l.Trim();
if(!thisLine.Contains(']'))
{
Console.Error.WriteLine("malformed directive line: ");
Console.WriteLine(thisLine);
continue;
}
if (thisLine.EndsWith(']'))
{
thisLine = thisLine.Trim('[', ']');
if (Uri.TryCreate(thisLine, UriKind.Absolute, out Uri asUri)
&& (asUri.Scheme == Uri.UriSchemeHttp || asUri.Scheme == Uri.UriSchemeHttps))
{
2024-08-07 20:04:28 -04:00
Console.WriteLine($"uri: {asUri}. ship off to yt-dlp or project Ose?");
}
else
{
//just a directive, we can't do anything here
Console.WriteLine($"mere directive: {thisLine}");
}
}
else
{
thisLine = thisLine.TrimStart('[');
var directiveName = thisLine[..thisLine.IndexOf(']')];
var parameterText = thisLine[(thisLine.IndexOf(']') + 1) ..].Trim();
if(directiveName == "note")
{
2024-08-07 20:04:28 -04:00
noteLines.Add(parameterText);
Console.WriteLine($"on screen note: {parameterText}");
}
else
{
2024-08-07 20:04:28 -04:00
altVOLines.Add(parameterText);
Console.WriteLine($"alt VA: {parameterText}");
}
}
}
2024-09-20 21:23:08 -04:00
if(altVOLines?.Count > 0)
{
var altVOscript = $"{scriptBasename}-altVO.txt";
File.WriteAllLines(altVOscript, altVOLines);
taskHeap.Add(ttsVO(altVOscript, Path.Combine(conf.VODropoff, $"altVO")));
2024-09-20 21:23:08 -04:00
}
2024-08-07 20:04:28 -04:00
// execute them all
taskHeap.Add(Task.Run(FFStuff));
taskHeap.Add(Task.Run(filePickup));
cancelSource.Cancel();
Task.WaitAll([.. taskHeap]);
Console.WriteLine("kbai");
}
2024-09-20 21:23:08 -04:00
private static async Task ttsVO(string scriptPath, string destPath)
2024-09-20 21:23:08 -04:00
{
HttpContent fileStreamContent = new StreamContent(File.OpenRead(scriptPath));
2024-09-20 21:23:08 -04:00
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 fname = await response.Content.ReadAsStringAsync();
Console.WriteLine($"success; {fname}."); //"success" might be a strong word; it's successfully submitted. Its decided what filename it will, eventually, give it.
var ext = Path.GetExtension(fname);
Directory.CreateDirectory(conf.VODropoff);
awaitFile(Path.Combine(conf.sync_dropoff, fname), destPath + $".{ext}");
2024-09-20 21:23:08 -04:00
}
}
}
}
}