2024-07-14 17:20:34 -04:00
using System.Collections.Concurrent ;
using System.Data ;
using System.Diagnostics ;
using System.Net ;
using System.Reflection.Metadata.Ecma335 ;
using System.Runtime.CompilerServices ;
2024-09-22 18:31:51 -04:00
using System.Runtime.InteropServices.Marshalling ;
2024-07-14 17:20:34 -04:00
using System.Text ;
using System.Text.RegularExpressions ;
using System.Timers ;
2024-07-23 18:42:50 -04:00
using System.Xml.Schema ;
2024-07-14 17:20:34 -04:00
using Newtonsoft.Json ;
namespace placeholdervo
{
public partial class Program
{
2024-07-23 18:42:50 -04:00
[GeneratedRegex("^(\\[|#)")]
private static partial Regex NonTextLine ( ) ;
2024-07-14 17:20:34 -04:00
public static Config conf ;
private static string workingDir = "./" ;
2024-09-20 21:23:08 -04:00
private static HttpClient hc ;
2024-07-14 17:20:34 -04:00
2024-07-23 18:42:50 -04:00
private static ConcurrentQueue < string > titleCardLines = new ConcurrentQueue < string > ( ) ;
2024-09-22 18:31:51 -04:00
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 > ( ) ;
2024-07-14 17:20:34 -04:00
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 )
{
2024-09-22 23:47:54 -04:00
//Console.WriteLine($"hoping for {srcKey} (would move it to {filePickups[srcKey]})");
2024-09-22 17:46:17 -04:00
if ( File . Exists ( srcKey ) )
{
Console . WriteLine ( $"found {srcKey}" ) ;
string dest ;
if ( filePickups . TryRemove ( srcKey , out dest ) )
{
2024-09-22 23:47:54 -04:00
File . Move ( srcKey , dest , true ) ;
2024-09-22 17:46:17 -04:00
}
else
{
Console . Error . WriteLine ( $"Failed to remove {srcKey} from filepickups. Unrecoverable, I think." ) ;
Environment . Exit ( - 1 ) ;
}
}
}
if ( scriptFileCancellationToken . IsCancellationRequested )
{
2024-09-22 23:47:54 -04:00
//Console.WriteLine($"Cancellation requested. but I'm using it in more of a \"there won't be more work, but finish what you're doing\" kind of way.");
if ( filePickups . IsEmpty )
{
Console . WriteLine ( "and we got nothing left, so we quit." ) ;
return ;
}
2024-09-22 17:46:17 -04:00
}
2024-07-14 17:20:34 -04:00
2024-09-22 17:46:17 -04:00
System . Threading . Thread . Sleep ( TimeSpan . FromSeconds ( 5 ) ) ;
}
}
2024-07-14 17:20:34 -04:00
2024-09-22 18:31:51 -04:00
private static async void FFStuff ( )
{
var cardCount = 0 ;
2024-09-22 23:47:54 -04:00
var magnitude = 1 ;
Console . WriteLine ( $"{titleCardLines.Count} title card lines" ) ;
magnitude = ( int ) Math . Log10 ( titleCardLines . Count ) ;
Directory . CreateDirectory ( conf . titlecards_dest ) ;
2024-09-22 18:31:51 -04:00
foreach ( var tcL in titleCardLines )
{
2024-09-22 23:47:54 -04:00
Console . WriteLine ( "titlecard commanding" ) ;
2024-09-22 18:31:51 -04:00
await Process . Start ( conf . titlecard_command , tcL ) . WaitForExitAsync ( ) ;
2024-09-22 23:47:54 -04:00
cardCount + + ;
File . Move ( "tc.png" , Path . Combine ( conf . titlecards_dest , $"tc{cardCount.ToString($" D { magnitude } ")}.png" ) , true ) ;
2024-09-22 18:31:51 -04:00
}
2024-09-22 23:47:54 -04:00
Console . WriteLine ( "titlecards done" ) ;
2024-09-22 18:31:51 -04:00
2024-09-22 23:47:54 -04:00
if ( noteLines ! = null )
2024-09-22 18:31:51 -04:00
{
2024-09-22 23:47:54 -04:00
cardCount = 0 ;
Console . WriteLine ( $"{noteLines?.Count} note lines" ) ;
magnitude = ( int ) Math . Log10 ( noteLines ? . Count ? ? 1 ) ;
Directory . CreateDirectory ( conf . notes_dest ) ;
foreach ( var nL in noteLines )
{
Console . WriteLine ( "note commanding" ) ;
await Process . Start ( conf . note_command , nL ) . WaitForExitAsync ( ) ;
cardCount + + ;
File . Move ( "note.png" , Path . Combine ( conf . notes_dest , $"note{cardCount.ToString($" D { magnitude } ")}.png" ) , true ) ;
}
2024-09-22 18:31:51 -04:00
}
}
2024-07-14 17:20:34 -04:00
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 ) } ;
2024-09-22 18:31:51 -04:00
var taskHeap = new List < Task > ( ) ;
2024-09-20 21:23:08 -04:00
2024-07-14 17:20:34 -04:00
var scriptPath = args ? . FirstOrDefault ( ) ;
if ( string . IsNullOrWhiteSpace ( scriptPath ) )
2024-07-23 18:42:50 -04:00
{
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 ) ;
2024-07-14 17:20:34 -04:00
workingDir = Path . GetDirectoryName ( scriptPath ) ;
var cancelSource = new CancellationTokenSource ( ) ;
scriptFileCancellationToken = cancelSource . Token ;
2024-07-23 18:42:50 -04:00
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 > ( ) ;
2024-09-22 23:47:54 -04:00
2024-07-23 18:42:50 -04:00
var directiveLines = new List < string > ( ) ;
2024-08-07 20:04:28 -04:00
var altVOLines = new List < string > ( ) ;
2024-09-22 23:47:54 -04:00
2024-07-14 17:20:34 -04:00
foreach ( var l in lines )
{
2024-07-23 18:42:50 -04:00
if ( l . StartsWith ( '#' ) )
2024-07-14 17:20:34 -04:00
{
2024-08-07 20:04:28 -04:00
titlecardUnprocessed . Add ( l ) ;
2024-07-23 18:42:50 -04:00
}
else if ( l . StartsWith ( '[' ) )
{
directiveLines . Add ( l ) ;
2024-07-14 17:20:34 -04:00
}
else
{
2024-07-23 18:42:50 -04:00
//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 ) ;
2024-07-14 17:20:34 -04:00
}
}
2024-07-23 18:42:50 -04:00
var scriptStripped = $"{scriptBasename}-primaryVO.txt" ;
File . WriteAllLines ( scriptStripped , primaryVOLines ) ;
2024-09-22 18:31:51 -04:00
taskHeap . Add ( ttsVO ( scriptStripped , Path . Combine ( conf . VODropoff , $"primaryVO" ) ) ) ;
2024-09-20 21:23:08 -04:00
2024-07-23 18:42:50 -04:00
var titleLevels = new List < int > ( ) { 0 } ;
var titleIncrementDepth = 0 ;
2024-08-07 20:04:28 -04:00
foreach ( var l in titlecardUnprocessed )
2024-07-14 17:20:34 -04:00
{
2024-07-23 18:42:50 -04:00
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-09-22 23:47:54 -04:00
titleCardLines . Enqueue ( TitleCardText ) ;
2024-07-23 18:42:50 -04:00
}
else
{
titleIncrementDepth + + ;
}
}
2024-07-14 17:20:34 -04:00
}
2024-07-23 18:42:50 -04:00
foreach ( var l in directiveLines )
2024-07-14 17:20:34 -04:00
{
2024-07-23 18:42:50 -04:00
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?" ) ;
2024-07-23 18:42:50 -04:00
}
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-09-22 23:47:54 -04:00
noteLines . Enqueue ( parameterText ) ;
2024-07-23 18:42:50 -04:00
Console . WriteLine ( $"on screen note: {parameterText}" ) ;
}
else
{
2024-08-07 20:04:28 -04:00
altVOLines . Add ( parameterText ) ;
2024-07-23 18:42:50 -04:00
Console . WriteLine ( $"alt VA: {parameterText}" ) ;
}
}
2024-07-14 17:20:34 -04:00
}
2024-09-20 21:23:08 -04:00
if ( altVOLines ? . Count > 0 )
{
2024-09-22 18:31:51 -04:00
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
2024-09-22 18:31:51 -04:00
taskHeap . Add ( Task . Run ( FFStuff ) ) ;
taskHeap . Add ( Task . Run ( filePickup ) ) ;
2024-09-22 23:47:54 -04:00
Console . WriteLine ( "sleeping for 1 sec before requesting cancellation" ) ;
System . Threading . Thread . Sleep ( TimeSpan . FromSeconds ( 1 ) ) ;
2024-09-22 18:31:51 -04:00
cancelSource . Cancel ( ) ;
Task . WaitAll ( [ . . taskHeap ] ) ;
Console . WriteLine ( "kbai" ) ;
2024-07-14 17:20:34 -04:00
}
2024-09-20 21:23:08 -04:00
2024-09-22 18:31:51 -04:00
private static async Task ttsVO ( string scriptPath , string destPath )
2024-09-20 21:23:08 -04:00
{
2024-09-22 18:31:51 -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
{
2024-09-22 23:47:54 -04:00
var fname = ( await response . Content . ReadAsStringAsync ( ) ) . Trim ( '[' ) . Trim ( ']' ) . Trim ( '"' ) ;
2024-09-22 18:31:51 -04:00
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 ) ;
2024-09-22 23:47:54 -04:00
awaitFile ( Path . Combine ( conf . sync_dropoff , fname ) , destPath + $"{ext}" ) ;
2024-09-20 21:23:08 -04:00
}
}
}
2024-07-14 17:20:34 -04:00
}
}