Skip to content

Commit

Permalink
Merge pull request #22 from StartAutomating/obs-powershell-updates
Browse files Browse the repository at this point in the history
obs-powershell 0.1.1
  • Loading branch information
StartAutomating authored Dec 12, 2022
2 parents 15d9742 + 98cf295 commit 06e5253
Show file tree
Hide file tree
Showing 298 changed files with 7,413 additions and 13,753 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
## obs-powershell 0.1.1

* Connect-OBS now caches connections (Fixes #18)
* Adding new core commands:
* Watch-OBS (Fixes #19)
* Receive-OBS (Fixes #20)
* Send-OBS (Fixes #21)
* All commands now support -PassThru (Fixes #16)
* All commands now increment requests correctly (Fixes #15)
* Improved formatting:
* Get-OBSScene (Fixes #14)
* Get-OBSSceneItem (Fixes #17)

---

## obs-powershell 0.1

Initial Release of obs-powershell
Expand Down
239 changes: 40 additions & 199 deletions Commands/Connect-OBS.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,225 +12,66 @@ function Connect-OBS
.LINK
Disconnect-OBS
#>
[CmdletBinding(DefaultParameterSetName='ExistingConnections')]
param(
# A credential describing the connection.
# The username should be the IPAddress, and the password should be the obs-websocket password.
[Parameter(ValueFromPipelineByPropertyName)]
[Management.Automation.PSCredential]
$Credential,

# The websocket password.
# You can see the websocket password in Tools -> obs-websocket settings -> show connect info
[Parameter(ValueFromPipelineByPropertyName)]
[securestring]
$WebSocketPassword,

# The websocket URL. If not provided, this will default to loopback on port 4455.
[Parameter(ValueFromPipelineByPropertyName)]
# The OBS websocket URL. If not provided, this will default to loopback on port 4455.
[Parameter(ValueFromPipelineByPropertyName,ParameterSetName='NewConnection')]
[ValidateScript({
if ($_.Scheme -ne 'ws') {
throw "Not a websocket uri"
}
return $true
})]
[uri]
$WebSocketUri = "ws://$([ipaddress]::Loopback):4455"
)

begin {
$obsWatcherJobDefinition = {
param([Management.Automation.PSCredential]$Credential)

function OBSIdentify {
$secret = "$obsPwd$($messageData.d.authentication.salt)"
$enc = [Security.Cryptography.SHA256Managed]::new()
$secretSalted64 = [Convert]::ToBase64String(
$enc.ComputeHash([Text.Encoding]::ascii.GetBytes($secret)
))

$saltedSecretAndChallenge = "$secretSalted64$(
$messageData.d.authentication.challenge
)"

$enc = [Security.Cryptography.SHA256Managed]::new()
$challenge64 = [Convert]::ToBase64String(
$enc.ComputeHash([Text.Encoding]::ascii.GetBytes(
$saltedSecretAndChallenge
))
)

$identifyMessage = [Ordered]@{
op = 1
d = [Ordered]@{
rpcVersion = 1
authentication = $challenge64
}
}
$PayloadJson = $identifyMessage | ConvertTo-Json -Compress
$SendSegment = [ArraySegment[Byte]]::new([Text.Encoding]::UTF8.GetBytes($PayloadJson))
$null = $Websocket.SendAsync($SendSegment,'Text', $true, [Threading.CancellationToken]::new($false))
}
$WebSocketUri = "ws://$([ipaddress]::Loopback):4455",

$webSocketUri = $Credential.UserName
$WebSocketPassword = $Credential.GetNetworkCredential().Password
$Websocket = [Net.WebSockets.ClientWebSocket]::new() # [Net.WebSockets.ClientWebSocket]::new()
$waitFor = [Timespan]'00:00:05'
$ConnectTask = $Websocket.ConnectAsync($webSocketUri, [Threading.CancellationToken]::new($false))
$null = $ConnectTask.ConfigureAwait($false)


$obsPwd = $WebSocketPassword
$WaitInterval = [Timespan]::FromMilliseconds(7)

$BufferSize = 16kb

$maxWaitTime = [DateTime]::Now + $WaitFor
while (!$ConnectTask.IsCompleted -and [DateTime]::Now -lt $maxWaitTime) {
# A randomly generated password used to connect to OBS.
# You can see the websocket password in Tools -> obs-websocket settings -> show connect info
[Parameter(ValueFromPipelineByPropertyName,ParameterSetName='NewConnection')]
[Alias('WebSocketPassword')]
[string]
$WebSocketToken
)

}

$Websocket

try {

while ($true) {
# Create a buffer for the response
$buffer = [byte[]]::new($BufferSize)
$receiveSegment = [ArraySegment[byte]]::new($buffer)
if (!($Websocket.State -eq 'Open')) {
throw 'Websocket is not open anymore. {0}' -f $Websocket.State
}
# Receive the next response from the WebSocket,
$receiveTask = $Websocket.ReceiveAsync($receiveSegment, [Threading.CancellationToken]::new($false))
# then wait for it to complete.
while (-not $receiveTask.IsCompleted) { Start-Sleep -Milliseconds $WaitInterval.TotalMilliseconds }
# "Receiving $($receiveTask.Result.Count)" | Out-Host
$msg = # Get the response and trim with extreme prejudice.
[Text.Encoding]::UTF8.GetString($buffer, 0, $receiveTask.Result.Count).Trim() -replace '\s+$'

if ($msg) {
$messageData = ConvertFrom-Json $msg
$MessageData.pstypenames.insert(0,'OBS.WebSocket.Message')
$newEventSplat = @{}

if ($messageData.op -eq 0 -and $messageData.d.authentication) {
. OBSIdentify
}

$newEventSplat.SourceIdentifier = 'OBS.WebSocket.Message'
$newEventSplat.MessageData = $MessageData

New-Event @newEventSplat

if ($messageData.op -eq 5) {
$newEventSplat = @{}
$newEventSplat.SourceIdentifier = "OBS.Event.$($messageData.d.eventType)"
if ($messageData.d.eventData) {
$newEventSplat.MessageData = [PSObject]::new($messageData.d.eventData)
$newEventSplat.MessageData.pstypenames.insert(0,"OBS.$($MessageData.d.eventType).response")
$newEventSplat.MessageData.pstypenames.insert(0,"$($newEventSplat.SourceIdentifier)")
}
New-Event @newEventSplat
}

if ($messageData.op -eq 7) {
$newEventSplat = @{}
$newEventSplat.SourceIdentifier = $MessageData.d.requestId
if ($messageData.d.responseData) {
$newEventSplat.MessageData = [PSObject]::new($MessageData.d.responseData)
$newEventSplat.MessageData.pstypenames.insert(0,"OBS.$($MessageData.d.requestType).response")
$newEventSplat.MessageData.pstypenames.insert(0,"$($newEventSplat.SourceIdentifier)")
}
New-Event @newEventSplat
}

}

$buffer.Clear()

}

} catch {
Write-Error -Exception $_.Exception -Message "StreamDeck Exception: $($_ | Out-String)" -ErrorId "WebSocket.State.$($Websocket.State)"
}
}


if (-not $script:ObsConnections) {
$script:ObsConnections = [Ordered]@{}
begin {
if ($home) {
$obsPowerShellRoot = Join-Path $home '.obs-powershell'
}
}

process {
if (-not $Credential) {
if ($WebSocketPassword) {
$Credential = [Management.Automation.PSCredential]::new($WebSocketUri, $WebSocketPassword)
process {
if ($PSCmdlet.ParameterSetName -eq 'NewConnection') {
$connectionExists = $script:ObsConnections[$WebSocketUri]
if ($connectionExists -and
$connectionExists.State -eq 'Running' -and
$connectionExists.WebSocket.State -eq 'Open'
) {
$connectionExists
Write-Verbose "Already connected"
return
}
}

if (-not $Credential) {
Write-Error "Must provide -Credential or -WebSocketPassword"
Watch-OBS @PSBoundParameters
return
}

$connectionExists = $script:ObsConnections[$Credential.UserName]
if ($connectionExists -and
$connectionExists.State -eq 'Running' -and
$connectionExists.WebSocket.State -eq 'Open'
) {
$connectionExists
Write-Verbose "Already connected"
return
}

$obsWatcher =
Start-ThreadJob -ScriptBlock $obsWatcherJobDefinition -Name "OBS.Connection.$($Credential.UserName)" -ArgumentList $Credential

$whenOutputAddedHandler =
Register-ObjectEvent -InputObject $obsWatcher.Output -EventName DataAdded -Action {

$dataAdded = $sender[$event.SourceArgs[1].Index]
if ($dataAdded -is [Management.Automation.PSEventArgs]) {
$newEventSplat = [Ordered]@{
SourceIdentifier = $dataAdded.SourceIdentifier
}
$newEventSplat.Sender = $eventSubscriber
if ($dataAdded.MessageData) {
$newEventSplat.MessageData = $dataAdded.MessageData
elseif ($PSCmdlet.ParameterSetName -eq 'ExistingConnections') {
if ($script:ObsConnections.Count) {
$script:ObsConnections.Values
} else {
$storedSockets = Get-ChildItem -path $obsPowerShellRoot -ErrorAction SilentlyContinue -Filter *.obs-websocket.clixml

if (-not $storedSockets) {
[PSCustomObject][Ordered]@{
PSTypeName = 'obs-powershell.not.connected'
Message = 'No connections to OBS. Use Connect-OBS to connect.'
}
if ($dataAdded.SourceEventArgs) {
$newEventSplat.EventArguments = $dataAdded.SourceEventArgs
}
$messageData = $dataAdded.MessageData
New-Event @newEventSplat
}
elseif ($dataAdded -is [Net.WebSockets.ClientWebSocket]) {
$eventSubscriber | Add-Member NoteProperty WebSocket $dataAdded -Force -PassThru
}
else {

} else {
$storedSockets |
Import-Clixml |
Watch-OBS
}
}

$whenOutputAddedHandler | Add-Member NoteProperty Watcher $obsWatcher -Force

$whenOutputAdded = @(
foreach ($subscriber in Get-EventSubscriber) {
if ($subscriber.Action -eq $whenOutputAddedHandler) {
$subscriber;break
}
}
)

$obsWatcher | Add-Member NoteProperty WhenOutputAddedHandler $whenOutputAddedHandler -Force
$obsWatcher | Add-Member NoteProperty WhenOutputAdded $whenOutputAdded -Force
$obsWatcher | Add-Member NoteProperty StartTime ([datetime]::Now) -Force
$obsWatcher | Add-Member ScriptProperty WebSocket {
$this.WhenOutputAdded.WebSocket
} -Force

$obsWatcher.pstypenames.insert(0, 'OBS.Connection')
$script:ObsConnections[$Credential.UserName] = $obsWatcher
$obsWatcher
}
}
}
Loading

0 comments on commit 06e5253

Please sign in to comment.