cm-pony / Experienceless

A Shadowplay API client
MIT License
39 stars 5 forks source link

Would you like to create an Nvidia CLI API? #1

Closed ghost closed 3 years ago

ghost commented 3 years ago

I and others on the Internet would like to be able to control ShadowPlay programmatically using some kind of API. Would you like to create a small CLI application to interact with regular ShadowPlay so that you can enable or disable playback, or start or stop recording using CMD?

cm-pony commented 3 years ago

If you have ShadowPlay installed you already have some kind of API. GFE uses Nodejs backend with Express server, it runs at http://localhost, basically it's rest api. You can check it in C:\Program Files (x86)\NVIDIA Corporation\NvNode (should be static path for x64 systems). ShadowPlay related stuff stored in "NvShadowPlayAPI.js"

So in order to communicate with it you need to know secret token and port - both of them are random with every launch and stored in memory mapped file called {8BA1E16C-FC54-4595-9782-E370A5FBE8DA}

If I understand correctly you can't read memory mapped file from native cmd but you can from powershell.

This script should work, it can start\stop manual record and save replay you can run it with command "Powershell.exe -File "Shadowplay.ps1 start"

function GetConfig
{
    $config_handle = [System.IO.MemoryMappedFiles.MemoryMappedFile]::OpenExisting('{8BA1E16C-FC54-4595-9782-E370A5FBE8DA}');
    $stream = $config_handle.CreateViewStream();
    $stream_reader = New-Object System.IO.StreamReader -ArgumentList $stream;
    $config_data = $stream_reader.ReadToEnd().Replace("`0", "");
    $stream_reader.Dispose();
    $stream.Dispose();
    return $config_data | ConvertFrom-Json;
}

$config = GetConfig

function SetRecording
{
    Param($state)
    $headers = @{"X_LOCAL_SECURITY_COOKIE" = $config.secret};
    $body = @{status = $state} | ConvertTo-Json;
    $uri = "http://localhost:$($config.port)/ShadowPlay/v.1.0/Record/Enable";
    Invoke-RestMethod -Uri $uri -Method "Post" -ContentType "application/json" -Headers $headers -Body $body;
}

function SaveIR
{
    $headers = @{"X_LOCAL_SECURITY_COOKIE" = $config.secret};
    $uri = "http://localhost:$($config.port)/ShadowPlay/v.1.0/InstantReplay/Save";
    Invoke-RestMethod -Uri $uri -Method "Post" -Headers $headers;
}

if ($args[0] -eq "start"){
  SetRecording $true;
} elseif ($args[0] -eq "stop"){
  SetRecording $false;
} elseif ($args[0] -eq "replay"){
  SaveIR;
} else {
  write-host("Command line arguments:");
  write-host("start                 start manual record");
  write-host("stop                  stop and save manual record");
  write-host("replay                save instant replay");
}
ghost commented 3 years ago

If you have ShadowPlay installed you already have some kind of API. GFE uses Nodejs backend with Express server, it runs at http://localhost, basically it's rest api. You can check it in C:\Program Files (x86)\NVIDIA Corporation\NvNode (should be static path for x64 systems). ShadowPlay related stuff stored in "NvShadowPlayAPI.js"

So in order to communicate with it you need to know secret token and port - both of them are random with every launch and stored in memory mapped file called {8BA1E16C-FC54-4595-9782-E370A5FBE8DA}

If I understand correctly you can't read memory mapped file from native cmd but you can from powershell.

This script should work, it can start\stop manual record and save replay you can run it with command "Powershell.exe -File "Shadowplay.ps1 start"

function GetConfig
{
    $config_handle = [System.IO.MemoryMappedFiles.MemoryMappedFile]::OpenExisting('{8BA1E16C-FC54-4595-9782-E370A5FBE8DA}');
    $stream = $config_handle.CreateViewStream();
    $stream_reader = New-Object System.IO.StreamReader -ArgumentList $stream;
    $config_data = $stream_reader.ReadToEnd().Replace("`0", "");
    $stream_reader.Dispose();
    $stream.Dispose();
    return $config_data | ConvertFrom-Json;
}

$config = GetConfig

function SetRecording
{
    Param($state)
    $headers = @{"X_LOCAL_SECURITY_COOKIE" = $config.secret};
    $body = @{status = $state} | ConvertTo-Json;
    $uri = "http://localhost:$($config.port)/ShadowPlay/v.1.0/Record/Enable";
    Invoke-RestMethod -Uri $uri -Method "Post" -ContentType "application/json" -Headers $headers -Body $body;
}

function SaveIR
{
    $headers = @{"X_LOCAL_SECURITY_COOKIE" = $config.secret};
    $uri = "http://localhost:$($config.port)/ShadowPlay/v.1.0/InstantReplay/Save";
    Invoke-RestMethod -Uri $uri -Method "Post" -Headers $headers;
}

if ($args[0] -eq "start"){
  SetRecording $true;
} elseif ($args[0] -eq "stop"){
  SetRecording $false;
} elseif ($args[0] -eq "replay"){
  SaveIR;
} else {
  write-host("Command line arguments:");
  write-host("start                 start manual record");
  write-host("stop                  stop and save manual record");
  write-host("replay                save instant replay");
}

You are a legend! Thanks bro! Do you know what is the URI to enable and disable "Instant Replay"?

cm-pony commented 3 years ago
POST: "/ShadowPlay/v.1.0/InstantReplay/Enable"
BODY: {"status": true/false}

You might want to toggle desktop capture as well

POST: "'/ShadowPlay/v.1.0/DesktopCapture/Enable"
BODY: {"enable": true/false}
ghost commented 3 years ago
POST: "/ShadowPlay/v.1.0/InstantReplay/Enable"
BODY: {"status": true/false}

You might want to toggle desktop capture as well

POST: "'/ShadowPlay/v.1.0/DesktopCapture/Enable"
BODY: {"enable": true/false}

Again, you're a legend! Thanks dude! I'll be able to slap something together with this info you've given me 😊. I've been wanting to automatically record using scripts for a while now. I'll probably write something in Python or Node.js to automatically start and stop recording when I open certain executables.

rajiteh commented 3 years ago

Just wanted to say this is super interesting information and it's a shame that it is tucked away in a github issue! 😅 Great job with this repo in general. Thanks for your hard work!