2024-06-24 18:11:36 -04:00
using System.Collections.Concurrent ;
using System.Text.Json ;
using CommonSocketLibrary.Abstract ;
using CommonSocketLibrary.Common ;
using Microsoft.Extensions.DependencyInjection ;
using Serilog ;
using TwitchChatTTS.OBS.Socket.Data ;
namespace TwitchChatTTS.OBS.Socket.Manager
{
public class OBSManager
{
2024-07-12 13:36:09 -04:00
private readonly IDictionary < string , RequestData > _requests ;
private readonly IDictionary < string , long > _sourceIds ;
private readonly IServiceProvider _serviceProvider ;
private readonly ILogger _logger ;
2024-06-24 18:11:36 -04:00
public OBSManager ( IServiceProvider serviceProvider , ILogger logger )
{
_serviceProvider = serviceProvider ;
_logger = logger ;
_requests = new ConcurrentDictionary < string , RequestData > ( ) ;
2024-07-12 13:36:09 -04:00
_sourceIds = new Dictionary < string , long > ( ) ;
2024-06-24 18:11:36 -04:00
}
2024-07-12 13:36:09 -04:00
public void AddSourceId ( string sourceName , long sourceId )
2024-06-24 18:11:36 -04:00
{
2024-07-12 13:36:09 -04:00
if ( ! _sourceIds . TryGetValue ( sourceName , out _ ) )
_sourceIds . Add ( sourceName , sourceId ) ;
2024-06-24 18:11:36 -04:00
else
2024-07-12 13:36:09 -04:00
_sourceIds [ sourceName ] = sourceId ;
_logger . Debug ( $"Added OBS scene item to cache [scene item: {sourceName}][scene item id: {sourceId}]" ) ;
}
2024-06-24 18:11:36 -04:00
2024-07-12 13:36:09 -04:00
public void ClearCache ( )
{
_sourceIds . Clear ( ) ;
2024-06-24 18:11:36 -04:00
}
public async Task Send ( IEnumerable < RequestMessage > messages )
{
string uid = GenerateUniqueIdentifier ( ) ;
2024-07-12 13:36:09 -04:00
var list = messages . ToList ( ) ;
_logger . Debug ( $"Sending OBS request batch of {list.Count} messages [obs request batch id: {uid}]." ) ;
2024-06-24 18:11:36 -04:00
// Keep track of requests to know what we requested.
2024-07-12 13:36:09 -04:00
foreach ( var message in list )
2024-06-24 18:11:36 -04:00
{
message . RequestId = GenerateUniqueIdentifier ( ) ;
var data = new RequestData ( message , uid ) ;
_requests . Add ( message . RequestId , data ) ;
}
2024-07-12 13:36:09 -04:00
_logger . Debug ( $"Generated uid for all OBS request messages in batch [obs request batch id: {uid}][obs request ids: {string.Join(" , ", list.Select(m => m.RequestType + " = " + m.RequestId))}]" ) ;
2024-06-24 18:11:36 -04:00
var client = _serviceProvider . GetRequiredKeyedService < SocketClient < WebSocketMessage > > ( "obs" ) ;
2024-07-12 13:36:09 -04:00
await client . Send ( 8 , new RequestBatchMessage ( uid , list ) ) ;
2024-06-24 18:11:36 -04:00
}
public async Task Send ( RequestMessage message , Action < Dictionary < string , object > > ? callback = null )
{
string uid = GenerateUniqueIdentifier ( ) ;
2024-07-06 23:42:33 -04:00
_logger . Debug ( $"Sending an OBS request [type: {message.RequestType}][obs request id: {uid}]" ) ;
2024-06-24 18:11:36 -04:00
// Keep track of requests to know what we requested.
message . RequestId = GenerateUniqueIdentifier ( ) ;
var data = new RequestData ( message , uid )
{
Callback = callback
} ;
_requests . Add ( message . RequestId , data ) ;
var client = _serviceProvider . GetRequiredKeyedService < SocketClient < WebSocketMessage > > ( "obs" ) ;
await client . Send ( 6 , message ) ;
}
public RequestData ? Take ( string id )
{
if ( id ! = null & & _requests . TryGetValue ( id , out var request ) )
{
_requests . Remove ( id ) ;
return request ;
}
return null ;
}
public async Task UpdateTransformation ( string sceneName , string sceneItemName , Action < OBSTransformationData > action )
{
2024-07-06 23:42:33 -04:00
if ( action = = null )
return ;
2024-06-24 18:11:36 -04:00
2024-07-06 23:42:33 -04:00
await GetSceneItemById ( sceneName , sceneItemName , async ( sceneItemId ) = >
{
2024-06-24 18:11:36 -04:00
var m2 = new RequestMessage ( "GetSceneItemTransform" , string . Empty , new Dictionary < string , object > ( ) { { "sceneName" , sceneName } , { "sceneItemId" , sceneItemId } } ) ;
await Send ( m2 , async ( d ) = >
{
2024-07-06 23:42:33 -04:00
if ( d = = null | | ! d . TryGetValue ( "sceneItemTransform" , out object? transformData ) | | transformData = = null )
2024-06-24 18:11:36 -04:00
return ;
2024-07-06 23:42:33 -04:00
_logger . Verbose ( $"Current transformation data [scene: {sceneName}][sceneItemName: {sceneItemName}][sceneItemId: {sceneItemId}][transform: {transformData}][obs request id: {m2.RequestId}]" ) ;
2024-06-24 18:11:36 -04:00
var transform = JsonSerializer . Deserialize < OBSTransformationData > ( transformData . ToString ( ) , new JsonSerializerOptions ( )
{
PropertyNameCaseInsensitive = false ,
PropertyNamingPolicy = JsonNamingPolicy . CamelCase
} ) ;
if ( transform = = null )
{
2024-07-06 23:42:33 -04:00
_logger . Warning ( $"Could not deserialize the transformation data received by OBS [scene: {sceneName}][sceneItemName: {sceneItemName}][sceneItemId: {sceneItemId}][obs request id: {m2.RequestId}]." ) ;
2024-06-24 18:11:36 -04:00
return ;
}
double w = transform . Width ;
double h = transform . Height ;
int a = transform . Alignment ;
bool hasBounds = transform . BoundsType ! = "OBS_BOUNDS_NONE" ;
if ( a ! = ( int ) OBSAlignment . Center )
{
if ( hasBounds )
transform . BoundsAlignment = a = ( int ) OBSAlignment . Center ;
else
transform . Alignment = a = ( int ) OBSAlignment . Center ;
2024-07-12 13:36:09 -04:00
2024-06-24 18:11:36 -04:00
transform . PositionX = transform . PositionX + w / 2 ;
transform . PositionY = transform . PositionY + h / 2 ;
}
2024-07-06 23:42:33 -04:00
// if (hasBounds)
// {
// // Take care of bounds, for most cases.
// // 'Crop to Bounding Box' might be unsupported.
// w = transform.BoundsWidth;
// h = transform.BoundsHeight;
// a = transform.BoundsAlignment;
// }
// else if (transform.CropBottom + transform.CropLeft + transform.CropRight + transform.CropTop > 0)
// {
// w -= transform.CropLeft + transform.CropRight;
// h -= transform.CropTop + transform.CropBottom;
// }
2024-06-24 18:11:36 -04:00
2024-07-06 23:42:33 -04:00
action ? . Invoke ( transform ) ;
2024-06-24 18:11:36 -04:00
2024-07-06 23:42:33 -04:00
var m3 = new RequestMessage ( "SetSceneItemTransform" , string . Empty , new Dictionary < string , object > ( ) { { "sceneName" , sceneName } , { "sceneItemId" , sceneItemId } , { "sceneItemTransform" , transform } } ) ;
await Send ( m3 ) ;
2024-07-12 13:36:09 -04:00
_logger . Debug ( $"New transformation data [scene: {sceneName}][sceneItemName: {sceneItemName}][sceneItemId: {sceneItemId}][obs request id: {m3.RequestId}]" ) ;
2024-07-06 23:42:33 -04:00
} ) ;
} ) ;
}
2024-06-24 18:11:36 -04:00
2024-07-06 23:42:33 -04:00
public async Task ToggleSceneItemVisibility ( string sceneName , string sceneItemName )
{
2024-07-12 13:36:09 -04:00
await GetSceneItemById ( sceneName , sceneItemName , async ( sceneItemId ) = >
2024-07-06 23:42:33 -04:00
{
2024-07-12 13:36:09 -04:00
var m1 = new RequestMessage ( "GetSceneItemEnabled" , string . Empty , new Dictionary < string , object > ( ) { { "sceneName" , sceneName } , { "sceneItemId" , sceneItemId } } ) ;
await Send ( m1 , async ( d ) = >
2024-07-06 23:42:33 -04:00
{
2024-07-12 13:36:09 -04:00
if ( d = = null | | ! d . TryGetValue ( "sceneItemEnabled" , out object? visible ) | | visible = = null )
return ;
2024-06-24 18:11:36 -04:00
2024-07-12 13:36:09 -04:00
var m2 = new RequestMessage ( "SetSceneItemEnabled" , string . Empty , new Dictionary < string , object > ( ) { { "sceneName" , sceneName } , { "sceneItemId" , sceneItemId } , { "sceneItemEnabled" , visible . ToString ( ) . ToLower ( ) = = "true" ? false : true } } ) ;
await Send ( m2 ) ;
2024-07-06 23:42:33 -04:00
} ) ;
2024-07-12 13:36:09 -04:00
} ) ;
2024-07-06 23:42:33 -04:00
}
2024-06-24 18:11:36 -04:00
2024-07-06 23:42:33 -04:00
public async Task UpdateSceneItemVisibility ( string sceneName , string sceneItemName , bool isVisible )
{
2024-07-12 13:36:09 -04:00
await GetSceneItemById ( sceneName , sceneItemName , async ( sceneItemId ) = >
2024-07-06 23:42:33 -04:00
{
2024-07-12 13:36:09 -04:00
var m = new RequestMessage ( "SetSceneItemEnabled" , string . Empty , new Dictionary < string , object > ( ) { { "sceneName" , sceneName } , { "sceneItemId" , sceneItemId } , { "sceneItemEnabled" , isVisible } } ) ;
await Send ( m ) ;
} ) ;
2024-07-06 23:42:33 -04:00
}
2024-06-24 18:11:36 -04:00
2024-07-06 23:42:33 -04:00
public async Task UpdateSceneItemIndex ( string sceneName , string sceneItemName , int index )
{
2024-07-12 13:36:09 -04:00
await GetSceneItemById ( sceneName , sceneItemName , async ( sceneItemId ) = >
2024-07-06 23:42:33 -04:00
{
2024-07-12 13:36:09 -04:00
var m = new RequestMessage ( "SetSceneItemIndex" , string . Empty , new Dictionary < string , object > ( ) { { "sceneName" , sceneName } , { "sceneItemId" , sceneItemId } , { "sceneItemIndex" , index } } ) ;
await Send ( m ) ;
} ) ;
}
public async Task GetGroupList ( Action < IEnumerable < string > > ? action )
{
var m = new RequestMessage ( "GetGroupList" , string . Empty , new Dictionary < string , object > ( ) ) ;
await Send ( m , ( d ) = >
{
if ( d = = null | | ! d . TryGetValue ( "groups" , out object? value ) | | value = = null )
return ;
var list = ( IEnumerable < string > ) value ;
_logger . Debug ( "Fetched the list of groups in OBS." ) ;
if ( list ! = null )
action ? . Invoke ( list ) ;
} ) ;
}
public async Task GetGroupSceneItemList ( string groupName , Action < IEnumerable < OBSSceneItem > > ? action )
{
var m = new RequestMessage ( "GetGroupSceneItemList" , string . Empty , new Dictionary < string , object > ( ) { { "sceneName" , groupName } } ) ;
await Send ( m , ( d ) = >
{
if ( d = = null | | ! d . TryGetValue ( "sceneItems" , out object? value ) | | value = = null )
return ;
var list = ( IEnumerable < OBSSceneItem > ) value ;
_logger . Debug ( $"Fetched the list of OBS scene items in a group [group: {groupName}]" ) ;
if ( list ! = null )
action ? . Invoke ( list ) ;
} ) ;
}
public async Task GetGroupSceneItemList ( IEnumerable < string > groupNames )
{
var messages = groupNames . Select ( group = > new RequestMessage ( "GetGroupSceneItemList" , string . Empty , new Dictionary < string , object > ( ) { { "sceneName" , group } } ) ) ;
await Send ( messages ) ;
_logger . Debug ( $"Fetched the list of OBS scene items in all groups [groups: {string.Join(" , ", groupNames)}]" ) ;
2024-07-06 23:42:33 -04:00
}
2024-06-24 18:11:36 -04:00
2024-07-06 23:42:33 -04:00
private async Task GetSceneItemById ( string sceneName , string sceneItemName , Action < long > action )
{
2024-07-12 13:36:09 -04:00
if ( _sourceIds . TryGetValue ( sceneItemName , out long sourceId ) )
{
_logger . Debug ( $"Fetched scene item id from cache [scene: {sceneName}][scene item: {sceneItemName}][scene item id: {sourceId}]" ) ;
action . Invoke ( sourceId ) ;
return ;
}
var m = new RequestMessage ( "GetSceneItemId" , string . Empty , new Dictionary < string , object > ( ) { { "sceneName" , sceneName } , { "sourceName" , sceneItemName } } ) ;
await Send ( m , async ( d ) = >
2024-07-06 23:42:33 -04:00
{
if ( d = = null | | ! d . TryGetValue ( "sceneItemId" , out object? value ) | | value = = null | | ! long . TryParse ( value . ToString ( ) , out long sceneItemId ) )
return ;
2024-06-24 18:11:36 -04:00
2024-07-12 13:36:09 -04:00
_logger . Debug ( $"Fetched scene item id from OBS [scene: {sceneName}][scene item: {sceneItemName}][scene item id: {sceneItemId}][obs request id: {m.RequestId}]" ) ;
AddSourceId ( sceneItemName , sceneItemId ) ;
2024-07-06 23:42:33 -04:00
action . Invoke ( sceneItemId ) ;
2024-06-24 18:11:36 -04:00
} ) ;
}
private string GenerateUniqueIdentifier ( )
{
return Guid . NewGuid ( ) . ToString ( "N" ) ;
}
2024-07-06 23:42:33 -04:00
private void LogExceptions ( Action action , string description )
{
try
{
action . Invoke ( ) ;
}
catch ( Exception e )
{
_logger . Error ( e , description ) ;
}
}
2024-06-24 18:11:36 -04:00
}
public class RequestData
{
public RequestMessage Message { get ; }
public string ParentId { get ; }
public Dictionary < string , object > ResponseValues { get ; set ; }
public Action < Dictionary < string , object > > ? Callback { get ; set ; }
public RequestData ( RequestMessage message , string parentId )
{
Message = message ;
ParentId = parentId ;
}
}
}