Sunshine/assets/web/config.html
ReenigneArcher 91465ed257 Update config.html
- Fix some typos
- Reformat some tags
2022-02-09 19:44:05 -05:00

871 lines
30 KiB
HTML

<div id="app" class="container">
<h1 class="my-4">Configuration</h1>
<div class="form" v-if="config">
<!--Header-->
<ul class="nav nav-tabs">
<li class="nav-item" v-for="tab in tabs" :key="tab.id">
<a
class="nav-link"
:class="{'active': tab.id === currentTab}"
href="#"
@click="currentTab = tab.id"
>{{tab.name}}</a
>
</li>
</ul>
<!--General Tab-->
<div v-if="currentTab === 'general'" class="config-page">
<!--Sunshine Name-->
<div class="mb-3">
<label for="sunshine_name" class="form-label">Sunshine Name</label>
<input
type="text"
class="form-control"
id="sunshine_name"
placeholder="Sunshine"
v-model="config.sunshine_name"
/>
<div class="form-text">
The name displayed by Moonlight. If not specified, the PC's hostname is used
</div>
</div>
<!--Log Level-->
<div class="mb-3">
<label for="min_log_level" class="form-label">Log Level</label>
<select
id="min_log_level"
class="form-select"
v-model="config.min_log_level"
>
<option :value="0">Verbose</option>
<option :value="1">Debug</option>
<option :value="2">Info</option>
<option :value="3">Warning</option>
<option :value="4">Error</option>
<option :value="5">Fatal</option>
<option :value="6">None</option>
</select>
<div class="form-text">
The minimum log level printed to standard out
</div>
</div>
<!--Origin Web UI Allowed-->
<div class="mb-3">
<label for="origin_web_ui_allowed" class="form-label"
>Origin Web UI Allowed</label
>
<select
id="origin_web_ui_allowed"
class="form-select"
v-model="config.origin_web_ui_allowed"
>
<option value="pc">Only localhost may access Web UI</option>
<option value="lan">Only those in LAN may access Web UI</option>
<option value="wan">Anyone may access Web UI</option>
</select>
<div class="form-text">
The origin of the remote endpoint address that is not denied access to Web UI
</div>
</div>
<!--UPnP-->
<div class="mb-3">
<label for="upnp" class="form-label">UPnP</label>
<select id="upnp" class="form-select" v-model="config.upnp">
<option value="disabled">Disabled</option>
<option value="enabled">Enabled</option>
</select>
<div class="form-text">Automatically configure port forwarding</div>
</div>
<!--Gamepads-->
<div class="mb-3" v-if="platform === 'windows'">
<label for="gamepad" class="form-label">Gamepads</label>
<select id="gamepad" class="form-select" v-model="config.gamepad">
<option value="ds4">DS4 (PS4)</option>
<option value="x360">X360 (Xbox 360)</option>
</select>
<div class="form-text">Choose which type of gamepad to Emulate on the host</div>
</div>
<!--Ping Timeout-->
<div class="mb-3">
<label for="ping_timeout" class="form-label">Ping Timeout</label>
<input
type="text"
class="form-control"
id="ping_timeout"
placeholder="10000"
v-model="config.ping_timeout"
/>
<div class="form-text">
How long to wait in milliseconds for data from moonlight before shutting down the stream
</div>
</div>
<!--Advertised FPS and Resolutions-->
<div class="mb-3">
<label for="ping_timeout" class="form-label"
>Advertised Resolutions and FPS</label
>
<div class="resolutions-container">
<label>Resolutions</label>
<div class="resolutions d-flex flex-wrap">
<div
class="p-2 ms-item m-2 d-flex justify-content-between"
v-for="(r,i) in resolutions"
:key="r"
>
<span class="px-2">{{r}}</span>
<span style="cursor: pointer" @click="resolutions.splice(i,1)"
>&times;</span
>
</div>
<form
@submit.prevent="resolutions.push(resIn);resIn = '';"
class="d-flex align-items-center"
>
<input
type="text"
v-model="resIn"
required
pattern="[0-9]+x[0-9]+"
style="
border-top-right-radius: 0;
border-bottom-right-radius: 0;
"
class="form-control"
/>
<button
style="border-top-left-radius: 0; border-bottom-left-radius: 0"
class="btn btn-success"
>
+
</button>
</form>
</div>
</div>
<div class="fps-container">
<label>FPS</label>
<div class="fps d-flex flex-wrap">
<div
class="p-2 ms-item m-2 d-flex justify-content-between"
v-for="(f,i) in fps"
:key="f"
>
<span class="px-2">{{f}}</span>
<span style="cursor: pointer" @click="fps.splice(i,1)"
>&times;</span
>
</div>
<form
@submit.prevent="fps.push(fpsIn);fpsIn = '';"
class="d-flex align-items-center"
>
<input
type="text"
v-model="fpsIn"
required
pattern="[0-9]+"
style="
width: 6ch;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
"
class="form-control"
/>
<button
style="border-top-left-radius: 0; border-bottom-left-radius: 0"
class="btn btn-success"
>
+
</button>
</form>
</div>
</div>
<div class="form-text">
The display modes advertised by Sunshine<br />
Some versions of Moonlight, such as Moonlight-nx (Switch), rely on this list to ensure that the requested
resolutions and fps are supported.<br>
This setting does <b>not</b> change how the screen stream is sent to Moonlight
</div>
</div>
<!-- Mapping Key AltRight to Key Windows -->
<div class="mb-3">
<label for="mapkey" class="form-label"
>Map Right Alt key to Windows key</label
>
<select
id="mapkey"
class="form-select"
v-model="config.key_rightalt_to_key_win"
>
<option value="disabled">Disabled</option>
<option value="enabled">Enabled</option>
</select>
</div>
<div class="form-text">
It may be possible that you cannot send the Windows Key from Moonlight directly.<br />
In those cases it may be useful to make Sunshine think the Right Alt key is the Windows key
</div>
</div>
<!--Files Tab-->
<div v-if="currentTab === 'files'" class="config-page">
<!--Private Key-->
<div class="mb-3">
<label for="pkey" class="form-label">Private Key</label>
<input
type="text"
class="form-control"
id="pkey"
placeholder="/dir/pkey.pem"
v-model="config.pkey"
/>
<div class="form-text">The private key must be 2048 bits</div>
</div>
<!--Cert-->
<div class="mb-3">
<label for="cert" class="form-label">Cert</label>
<input
type="text"
class="form-control"
id="cert"
placeholder="/dir/cert.pem"
v-model="config.cert"
/>
<div class="form-text">
The certificate must be signed with a 2048 bit key
</div>
</div>
<!--State File-->
<div class="mb-3">
<label for="file_state" class="form-label">State File</label>
<input
type="text"
class="form-control"
id="file_state"
placeholder="sunshine_state.json"
v-model="config.file_state"
/>
<div class="form-text">
The file where current state of Sunshine is stored
</div>
</div>
<!--Apps File-->
<div class="mb-3">
<label for="file_apps" class="form-label">Apps File</label>
<input
type="text"
class="form-control"
id="file_apps"
placeholder="apps.json"
v-model="config.file_apps"
/>
<div class="form-text">
The file where current apps of Sunshine are stored
</div>
</div>
</div>
<div v-if="currentTab === 'input'" class="config-page">
<!--Back Button Timeout-->
<div class="mb-3">
<label for="back_button_timeout" class="form-label"
>Back Button Timeout</label
>
<input
type="text"
class="form-control"
id="back_button_timeout"
placeholder="2000"
v-model="config.back_button_timeout"
/>
<div class="form-text">
The back/select button on the controller.<br />
On the Shield, the home and power button are not passed to Moonlight.<br />
If, after the timeout, the back button is still pressed down, Home/Guide button press is emulated.<br />
If back_button_timeout &lt; 0, then the Home/Guide button will not be emulated<br />
</div>
</div>
<!-- Key Repeat Delay-->
<div class="mb-3" v-if="platform === 'windows'">
<label for="key_repeat_delay" class="form-label"
>Key Repeat Delay</label
>
<input
type="text"
class="form-control"
id="key_repeat_delay"
placeholder="500"
v-model="config.key_repeat_delay"
/>
<div class="form-text">
Control how fast keys will repeat themselves<br />
The initial delay in milliseconds before repeating keys
</div>
</div>
<!-- Key Repeat Frequency-->
<div class="mb-3" v-if="platform === 'windows'">
<label for="key_repeat_frequency" class="form-label"
>Key Repeat Frequency</label
>
<input
type="text"
class="form-control"
id="key_repeat_frequency"
placeholder="24.9"
v-model="config.key_repeat_frequency"
/>
<div class="form-text">
How often keys repeat every second<br />
This configurable option supports decimals
</div>
</div>
</div>
<!--Files Tab-->
<div v-if="currentTab === 'av'" class="config-page">
<!--Audio Sink-->
<div class="mb-3" v-if="platform === 'windows'">
<label for="audio_sink" class="form-label">Audio Sink</label>
<input
type="text"
class="form-control"
id="audio_sink"
placeholder="{0.0.0.00000000}.{FD47D9CC-4218-4135-9CE2-0C195C87405B}"
v-model="config.audio_sink"
/>
<div class="form-text">
The name of the audio sink used for Audio Loopback<br />
You can find the name of the audio sink using the following command:<br />
<pre>tools\audio-info.exe</pre>
</div>
</div>
<div class="mb-3" v-if="platform === 'linux'">
<label for="audio_sink" class="form-label">Audio Sink</label>
<input
type="text"
class="form-control"
id="audio_sink"
placeholder="alsa_output.pci-0000_09_00.3.analog-stereo"
v-model="config.audio_sink"
/>
<div class="form-text">
The name of the audio sink used for Audio Loopback<br />
If you do not specify this variable, pulseaudio will select the default monitor device.<br />
<br />
You can find the name of the audio sink using either command:<br />
<pre>pacmd list-sinks | grep "name:"</pre>
<pre>pactl info | grep Source</pre>
<br />
</div>
</div>
<!--Virtual Sink-->
<div class="mb-3" v-if="platform === 'windows'">
<label for="virtual_sink" class="form-label">Virtual Sink</label>
<input
type="text"
class="form-control"
id="virtual_sink"
placeholder="{0.0.0.00000000}.{8edba70c-1125-467c-b89c-15da389bc1d4}"
v-model="config.virtual_sink"
/>
<div class="form-text">
The virtual sink, is the audio device that's virtual (Like Steam Streaming Speakers), it allows Sunshine to
stream audio, while muting the speakers.
</div>
</div>
<!--Adapter Name -->
<div class="mb-3" v-if="platform === 'windows'">
<label for="adapter_name" class="form-label">Adapter Name</label>
<input
type="text"
class="form-control"
id="adapter_name"
placeholder="Radeon RX 580 Series"
v-model="config.adapter_name"
/>
<div class="form-text" v-if="platform === 'windows'">
You can select the video card you want to stream:<br />
The appropriate values can be found using the following command:<br />
<pre>tools\dxgi-info.exe</pre>
</div>
</div>
<!--Output Name -->
<div class="mb-3" class="config-page" v-if="platform === 'windows'">
<label for="output_name" class="form-label">Output Name</label>
<input
type="text"
class="form-control"
id="output_name"
placeholder="\\.\DISPLAY1"
v-model="config.output_name"
/>
<div class="form-text">
You can select the video card you want to stream:<br />
The appropriate values can be found using the following command:<br />
tools\dxgi-info.exe<br />
<br />
</div>
</div>
<div class="mb-3" class="config-page" v-if="platform === 'linux'">
<label for="output_name" class="form-label">Monitor number</label>
<input
type="text"
class="form-control"
id="output_name"
placeholder="0"
v-model="config.output_name"
/>
<div class="form-text">
xrandr --listmonitors<br />
Example output:
<pre> 0: +HDMI-1 1920/518x1200/324+0+0 HDMI-1</pre>
</div>
</div>
</div>
<div v-if="currentTab === 'advanced'" class="config-page">
<!--Port family-->
<div class="mb-3">
<label for="port" class="form-label">Port</label>
<input
type="number"
min="0"
max="65529"
class="form-control"
id="port"
placeholder="47989"
v-model="config.port"
/>
<div class="form-text">Set the family of ports used by Sunshine</div>
</div>
<!-- Quantization Parameter -->
<div class="mb-3">
<label for="qp" class="form-label">Quantitization Parameter</label>
<input
type="number"
class="form-control"
id="qp"
placeholder="28"
v-model="config.qp"
/>
<div class="form-text">
Quantitization Parameter<br />
Some devices may not support Constant Bit Rate.<br />
For those devices, QP is used instead.<br />
Higher value means more compression, but less quality<br />
</div>
</div>
<!-- Min Threads -->
<div class="mb-3">
<label for="min_threads" class="form-label"
>Minimum number of threads used by ffmpeg to encode the video.</label
>
<input
type="number"
min="1"
class="form-control"
id="min_threads"
placeholder="1"
v-model="config.min_threads"
/>
<div class="form-text">
Minimum number of threads used by ffmpeg to encode the video.<br />
Increasing the value slightly reduces encoding efficiency, but the tradeoff is usually<br />
worth it to gain the use of more CPU cores for encoding. The ideal value is the lowest<br />
value that can reliably encode at your desired streaming settings on your hardware.
</div>
</div>
<!--HEVC Support -->
<div class="mb-3">
<label for="hevc_mode" class="form-label">HEVC Support</label>
<select id="hevc_mode" class="form-select" v-model="config.hevc_mode">
<option value="0">
Sunshine will specify support for HEVC based on encoder
</option>
<option value="1">
Sunshine will not advertise support for HEVC
</option>
<option value="2">
Sunshine will advertise support for HEVC Main profile
</option>
<option value="3">
Sunshine will advertise support for HEVC Main and Main10 (HDR) profiles
</option>
</select>
<div class="form-text">
Allows the client to request HEVC Main or HEVC Main10 video streams.<br />
HEVC is more CPU-intensive to encode, so enabling this may reduce performance when using software encoding.
</div>
</div>
<!--Encoder -->
<div class="mb-3">
<label for="encoder" class="form-label">Force a Specific Encoder</label>
<select id="encoder" class="form-select" v-model="config.encoder">
<option :value="''">Autodetect</option>
<option value="nvenc">nVidia NVENC</option>
<option value="amdvce">AMD AMF/VCE</option>
<option value="vaapi">VA-API</option>
<option value="software">Software</option>
</select>
<div class="form-text">
Force a specific encoder, otherwise Sunshine will use the first encoder that is available
</div>
</div>
<!--FEC Percentage-->
<div class="mb-3">
<label for="fec_percentage" class="form-label">FEC Percentage</label>
<input
type="text"
class="form-control"
id="fec_percentage"
placeholder="20"
v-model="config.fec_percentage"
/>
<div class="form-text">
Percentage of error correcting packets per data packet in each video frame.<br />
Higher values can correct for more network packet loss, but at the cost of increasing bandwidth usage.<br />
The default value of 20 is what GeForce Experience uses.
</div>
</div>
<!--Channels-->
<div class="mb-3">
<label for="channels" class="form-label">Channels</label>
<input
type="text"
class="form-control"
id="channels"
placeholder="1"
v-model="config.channels"
/>
<div class="form-text">
When multicasting, it could be useful to have different configurations for each connected Client. For example:
<ul>
<li>
Clients connected through WAN and LAN have different bitrate constraints.
</li>
<li>
Decoders may require different settings for color
</li>
</ul>
Unlike simply broadcasting to multiple Client, this will generate distinct video streams.<br />
Note, CPU usage increases for each distinct video stream generated
</div>
</div>
<!--Credentials File-->
<div class="mb-3">
<label for="credentials_file" class="form-label"
>Web Manager Credentials File</label
>
<input
type="text"
class="form-control"
id="credentials_file"
placeholder="sunshine_state.json"
v-model="config.credentials_file"
/>
<div class="form-text">
Store Username/Password separately from Sunshine's state file.
</div>
</div>
<!--Origin PIN Allowed-->
<div class="mb-3">
<label for="origin_pin_allowed" class="form-label"
>Origin PIN Allowed</label
>
<select
id="origin_pin_allowed"
class="form-select"
v-model="config.origin_pin_allowed"
>
<option value="pc">Only localhost may access /pin</option>
<option value="lan">Only those in LAN may access /pin</option>
<option value="wan">Anyone may access /pin</option>
</select>
<div class="form-text">
The origin of the remote endpoint address that is not denied for HTTP method /pin
</div>
</div>
<!--External IP-->
<div class="mb-3">
<label for="external_ip" class="form-label">External IP</label>
<input
type="text"
class="form-control"
id="external_ip"
placeholder="123.456.789.12"
v-model="config.external_ip"
/>
<div class="form-text">
If no external IP address is given, Sunshine will automatically detect external IP
</div>
</div>
</div>
<!--Software Settings-->
<div v-if="currentTab === 'sw'" class="config-page">
<div class="mb-3">
<label for="sw_preset" class="form-label">SW Presets</label>
<input
class="form-control"
id="sw_preset"
placeholder="superfast"
v-model="config.sw_preset"
/>
<div class="form-text">
Available Values: ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
</div>
</div>
<div class="mb-3">
<label for="sw_tune" class="form-label">SW Tune</label>
<input
class="form-control"
id="sw_tune"
placeholder="zerolatency"
v-model="config.sw_tune"
/>
<div class="form-text">
Available Tune settings: <br>
film - use for high quality movie content; lowers deblocking<br>
animation - good for cartoons; uses higher deblocking and more reference frames<br>
grain - preserves the grain structure in old, grainy film material<br>
stillimage - good for slideshow-like content<br>
fastdecode - allows faster decoding by disabling certain filters<br>
zerolatency - good for fast encoding and low-latency streaming<br>
</div>
</div>
</div>
<!--Nvidia Encoder Settings-->
<div v-if="currentTab === 'nv'" class="config-page">
<!--NVENC SETTINGS-->
<div class="mb-3">
<label for="nv_preset" class="form-label">NVEnc Preset</label>
<select id="nv_preset" class="form-select" v-model="config.nv_preset">
<option value="default">Default</option>
<option value="hp">High Performance</option>
<option value="hq">High Quality</option>
<option value="slow">Slow - hq 2 passes</option>
<option value="medium">medium -- hq 1 pass</option>
<option value="fast">fast -- hp 1 pass</option>
<option value="bd">bd</option>
<option value="ll">ll -- low latency</option>
<option value="llhq">llhq</option>
<option value="llhp">llhp</option>
<option value="lossless">lossless</option>
<option value="losslesshp">losslesshp</option>
</select>
</div>
<div class="mb-3">
<label for="nv_rc" class="form-label">NVEnc Rate Control</label>
<select id="nv_rc" class="form-select" v-model="config.nv_rc">
<option value="auto">auto -- let ffmpeg decide rate control</option>
<option value="constqp">constqp -- constant QP mode</option>
<option value="vbr">vbr -- variable bitrate</option>
<option value="cbr">cbr -- constant bitrate</option>
<option value="cbr_hq">cbr_hq -- cbr high quality</option>
<option value="cbr_ld_hq">cbr_ld_hq -- cbr low delay high quality</option>
<option value="vbr_hq">vbr_hq -- vbr high quality</option>
</select>
</div>
<div class="mb-3">
<label for="nv_coder" class="form-label">NVEnc Coder</label>
<select id="nv_coder" class="form-select" v-model="config.nv_coder">
<option value="auto">auto</option>
<option value="cabac">cabac</option>
<option value="cavlc">cavlc</option>
</select>
</div>
</div>
<!--AMD Encoder Settings-->
<div v-if="currentTab === 'amd'" class="config-page">
<!--Presets-->
<div class="mb-3">
<label for="amd_quality" class="form-label">AMD AMF Quality</label>
<select
id="amd_quality"
class="form-select"
v-model="config.amd_quality"
>
<option value="default">Default</option>
<option value="speed">Speed</option>
<option value="balanced">Balanced</option>
</select>
</div>
<div class="mb-3">
<label for="amd_rc" class="form-label">AMD AMF Rate Control</label>
<select id="amd_rc" class="form-select" v-model="config.amd_rc">
<option value="auto">auto -- let ffmpeg decide rate control</option>
<option value="constqp">constqp -- constant QP mode</option>
<option value="vbr_latency">vbr_latency -- Latency Constrained Variable Bitrate</option>
<option value="vbr_peak">vbr_peak -- Peak Contrained Variable Bitrate</option>
<option value="cbr">cbr -- constant bitrate</option>
</select>
</div>
<div class="mb-3">
<label for="amd_coder" class="form-label">AMD AMF Rate Control</label>
<select id="amd_coder" class="form-select" v-model="config.amd_coder">
<option value="auto">auto</option>
<option value="cabac">cabac</option>
<option value="cavlc">cavlc</option>
</select>
</div>
</div>
<div v-if="currentTab === 'va-api'" class="config-page">
<input
class="form-control"
id="adapter_name"
placeholder="/dev/dri/renderD128"
v-model="config.adapter_name"
/>
</div>
</div>
<div class="alert alert-success my-4" v-if="success">
<b>Success!</b> Restart Sunshine to apply changes
</div>
<div class="mb-3 buttons">
<button class="btn btn-primary" @click="save">Save</button>
</div>
</div>
<script>
new Vue({
el: "#app",
data() {
return {
platform: "",
success: false,
config: null,
fps: [],
resolutions: [],
currentTab: "general",
resIn: "",
fpsIn: "",
tabs: [
{
id: "general",
name: "General",
},
{
id: "files",
name: "Files",
},
{
id: "input",
name: "Input",
},
{
id: "av",
name: "Audio/Video",
},
{
id: "advanced",
name: "Advanced",
},
{
id: "sw",
name: "Software Encoder",
},
{
id: "nv",
name: "NVENC Encoder",
},
{
id: "amd",
name: "AMF Encoder",
},
{
id: "va-api",
name: "VA-API encoder",
},
],
};
},
created() {
fetch("/api/config")
.then((r) => r.json())
.then((r) => {
this.config = r;
this.platform = this.config.platform;
var app = document.getElementById("app");
if (this.platform == "windows") {
this.tabs = this.tabs.filter((el) => {
return el.id !== "va-api";
});
}
if (this.platform == "linux") {
this.tabs = this.tabs.filter((el) => {
return el.id !== "amd";
});
}
delete this.config.status;
delete this.config.platform;
//Populate default values if not present in config
this.config.key_rightalt_to_key_win =
this.config.key_rightalt_to_key_win || "disabled";
this.config.gamepad = this.config.gamepad || "x360";
this.config.upnp = this.config.upnp || "disabled";
this.config.min_log_level = this.config.min_log_level || 2;
this.config.origin_pin_allowed =
this.config.origin_pin_allowed || "pc";
this.config.origin_web_ui_allowed =
this.config.origin_web_manager_allowed || "lan";
this.config.hevc_mode = this.config.hevc_mode || 0;
this.config.encoder = this.config.encoder || "";
this.config.nv_preset = this.config.nv_preset || "default";
this.config.nv_rc = this.config.nv_rc || "auto";
this.config.nv_coder = this.config.nv_coder || "auto";
this.config.amd_quality = this.config.amd_quality || "default";
this.config.amd_rc = this.config.amd_rc || "auto";
this.config.fps = this.config.fps || "[10, 30, 60, 90, 120]";
this.config.resolutions =
this.config.resolutions ||
"[352x240,480x360,858x480,1280x720,1920x1080,2560x1080,3440x1440,1920x1200,3860x2160,3840x1600]";
this.fps = JSON.parse(this.config.fps);
//Resolutions should be fixed because are not valid JSON
let res = this.config.resolutions.substring(
1,
this.config.resolutions.length - 1
);
let resolutions = [];
res.split(",").forEach((r) => resolutions.push(r.trim()));
this.resolutions = resolutions;
});
},
methods: {
save() {
this.success = false;
let nl = this.config === "windows" ? "\r\n" : "\n";
this.config.resolutions =
"[" +
nl +
" " +
this.resolutions.join("," + nl + " ") +
nl +
"]";
this.config.fps = JSON.stringify(this.fps);
fetch("/api/config", {
method: "POST",
body: JSON.stringify(this.config),
}).then((r) => {
if (r.status == 200) this.success = true;
});
},
},
});
</script>
<style>
.config-page {
padding: 1em;
border: 1px solid #dee2e6;
border-top: none;
}
.buttons {
padding: 1em 0;
}
.ms-item {
background-color: #ccc;
font-size: 12px;
font-weight: bold;
}
</style>