Compare commits

..

1 Commits

Author SHA1 Message Date
ReenigneArcher
5b863f760b feat(api): add openapi specification 2025-02-01 10:07:42 -05:00
68 changed files with 652 additions and 589 deletions

View File

@@ -1,10 +1,6 @@
# install dependencies for C++ analysis
set -e
# setup homebrew for x86_64
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
eval "$(/usr/local/bin/brew shellenv)"
# install dependencies
dependencies=(
"boost"

View File

@@ -5,15 +5,9 @@
blank_issues_enabled: false
contact_links:
- name: Discussions
url: https://github.com/orgs/LizardByte/discussions
about: Community discussions
- name: Questions
url: https://github.com/orgs/LizardByte/discussions
about: Ask questions
- name: Feature Requests
url: https://github.com/orgs/LizardByte/discussions
about: Request new features
- name: Support Center
url: https://app.lizardbyte.dev/support
about: Official LizardByte support
- name: Discussions
url: https://github.com/orgs/LizardByte/discussions
about: Community discussions, questions, and feature requests

View File

@@ -45,22 +45,43 @@ jobs:
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
setup_flatpak_matrix:
name: Setup Flatpak Matrix
runs-on: ubuntu-latest
steps:
- name: Set release details
id: flatpak_matrix
# https://www.cynkra.com/blog/2020-12-23-dynamic-gha
run: |
# determine which architectures to build
if [[ "${{ github.event_name }}" == "push" ]]; then
matrix=$((
echo '{ "arch" : ["x86_64", "aarch64"] }'
) | jq -c .)
else
matrix=$((
echo '{ "arch" : ["x86_64"] }'
) | jq -c .)
fi
echo $matrix
echo $matrix | jq .
echo "matrix=$matrix" >> $GITHUB_OUTPUT
outputs:
matrix: ${{ steps.flatpak_matrix.outputs.matrix }}
build_linux_flatpak:
env:
APP_ID: dev.lizardbyte.app.Sunshine
NODE_VERSION: "20"
PLATFORM_VERSION: "23.08"
name: Linux Flatpak
needs: [setup_release]
runs-on: ${{ matrix.runner }}
runs-on: ubuntu-22.04
needs: [setup_release, setup_flatpak_matrix]
strategy:
fail-fast: false # false to test all, true to fail entire job if any fail
matrix:
include:
- arch: x86_64
runner: ubuntu-22.04
- arch: aarch64
runner: ubuntu-22.04-arm
matrix: ${{fromJson(needs.setup_flatpak_matrix.outputs.matrix)}}
steps:
- name: Maximize build space
@@ -105,7 +126,8 @@ jobs:
sudo apt-get update -y
sudo apt-get install -y \
cmake \
flatpak
flatpak \
qemu-user-static
sudo su $(whoami) -c "flatpak --user remote-add --if-not-exists flathub \
https://flathub.org/repo/flathub.flatpakrepo"
@@ -205,23 +227,52 @@ jobs:
- name: Lint Flatpak
working-directory: build
run: |
exceptions_file="${{ github.workspace }}/packaging/linux/flatpak/exceptions.json"
echo "Linting flatpak manifest"
flatpak run --command=flatpak-builder-lint org.flatpak.Builder \
--exceptions \
--user-exceptions "${exceptions_file}" \
manifest \
${APP_ID}.yml
manifest ${APP_ID}.yml > _flatpak-lint-exceptions_manifest.json || true
echo "Linting flatpak repo"
# TODO: add arg
# --mirror-screenshots-url=https://dl.flathub.org/media \
flatpak run --command=flatpak-builder-lint org.flatpak.Builder \
--exceptions \
--user-exceptions "${exceptions_file}" \
repo \
repo
repo repo > _flatpak-lint-exceptions_repo.json || true
checks=(manifest repo)
exit_code=0
# check if files are equal
for check in "${checks[@]}"; do
echo "Validating $check"
# load baseline and result files
baseline="${{ github.workspace }}/packaging/linux/flatpak/flatpak-lint-baseline_${check}.json"
result="_flatpak-lint-exceptions_${check}.json"
# Extract errors from both JSON files
readarray -t result_errors < <(jq -r '.errors[]' "$result")
readarray -t baseline_errors < <(jq -r '.errors[]' "$baseline")
# Loop through result errors and check against baseline errors
for error in "${result_errors[@]}"; do
if printf '%s\n' "${baseline_errors[@]}" | grep -q -F "$error"; then
echo "::warning:: '$error'"
else
echo "::error:: '$error'"
exit_code=1
fi
done
done
# if exit code is not 0, print results
if [ $exit_code -ne 0 ]; then
echo "Manifest lint results:"
cat _flatpak-lint-exceptions_manifest.json
echo "Repo lint results:"
cat _flatpak-lint-exceptions_repo.json
fi
# exit with the correct code
exit $exit_code
- name: Package Flathub repo archive
# copy files required to generate the Flathub repo
@@ -503,16 +554,12 @@ jobs:
rm '/usr/local/bin/2to3-3.12'
rm '/usr/local/bin/idle3'
rm '/usr/local/bin/idle3.12'
rm '/usr/local/bin/idle3.13'
rm '/usr/local/bin/pydoc3'
rm '/usr/local/bin/pydoc3.12'
rm '/usr/local/bin/pydoc3.13'
rm '/usr/local/bin/python3'
rm '/usr/local/bin/python3.12'
rm '/usr/local/bin/python3.13'
rm '/usr/local/bin/python3-config'
rm '/usr/local/bin/python3.12'
rm '/usr/local/bin/python3.12-config'
rm '/usr/local/bin/python3.13-config'
brew install python
- name: Setup python

View File

@@ -123,7 +123,7 @@ jobs:
docker:
needs: [check_dockerfiles, setup_release]
if: ${{ needs.check_dockerfiles.outputs.dockerfiles }}
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
permissions:
packages: write
contents: write

View File

@@ -51,39 +51,32 @@ jobs:
"include": []
}
// Track languages we've already added to avoid duplicates
const addedLanguages = new Set()
for (let [key, value] of Object.entries(response.data)) {
// remap language
if (remap_languages[key.toLowerCase()]) {
console.log(`Remapping language: ${key} to ${remap_languages[key.toLowerCase()]}`)
key = remap_languages[key.toLowerCase()]
}
const normalizedKey = key.toLowerCase()
if (supported_languages.includes(normalizedKey) && !addedLanguages.has(normalizedKey)) {
// Mark this language as added
addedLanguages.add(normalizedKey)
console.log(`Found supported language: ${normalizedKey}`)
if (supported_languages.includes(key.toLowerCase())) {
console.log(`Found supported language: ${key}`)
let osList = ['ubuntu-latest'];
if (normalizedKey === 'swift') {
if (key.toLowerCase() === 'swift') {
osList = ['macos-latest'];
} else if (normalizedKey === 'cpp') {
osList = ['macos-latest', 'ubuntu-latest', 'windows-latest'];
} else if (key.toLowerCase() === 'cpp') {
// TODO: update macos to latest after the below issue is resolved
// https://github.com/github/codeql-action/issues/2266
osList = ['macos-13', 'ubuntu-latest', 'windows-latest'];
}
for (let os of osList) {
// set name for matrix
let name = osList.length === 1 ? normalizedKey : `${normalizedKey}, ${os}`
if (osList.length == 1) {
name = key.toLowerCase()
} else {
name = `${key.toLowerCase()}, ${os}`
}
// add to matrix
matrix['include'].push({
"language": normalizedKey,
"os": os,
"name": name
})
matrix['include'].push({"language": key.toLowerCase(), "os": os, "name": name})
}
}
}

View File

@@ -104,7 +104,7 @@ jobs:
if: >-
steps.check-label.outputs.hasTopic == 'true' &&
steps.check-release.outputs.isLatestRelease == 'true'
uses: robinraju/release-downloader@v1.12
uses: robinraju/release-downloader@v1.11
with:
repository: "${{ github.repository }}"
tag: "${{ github.event.release.tag_name }}"

View File

@@ -46,7 +46,7 @@ jobs:
id: download
if: >-
steps.check-label.outputs.hasTopic == 'true'
uses: robinraju/release-downloader@v1.12
uses: robinraju/release-downloader@v1.11
with:
repository: "${{ github.repository }}"
tag: "${{ github.event.release.tag_name }}"

View File

@@ -77,7 +77,7 @@ jobs:
if: >-
steps.check-label.outputs.hasTopic == 'true' &&
steps.check-release.outputs.isLatestRelease == 'true'
uses: robinraju/release-downloader@v1.12
uses: robinraju/release-downloader@v1.11
with:
repository: "${{ github.repository }}"
tag: "${{ github.event.release.tag_name }}"

View File

@@ -46,7 +46,7 @@ jobs:
id: download
if: >-
steps.check-label.outputs.hasTopic == 'true'
uses: robinraju/release-downloader@v1.12
uses: robinraju/release-downloader@v1.11
with:
repository: "${{ github.repository }}"
tag: "${{ github.event.release.tag_name }}"

6
.gitmodules vendored
View File

@@ -44,7 +44,7 @@
branch = sdk
[submodule "third-party/Simple-Web-Server"]
path = third-party/Simple-Web-Server
url = https://github.com/LizardByte-infrastructure/Simple-Web-Server.git
url = https://gitlab.com/eidheim/Simple-Web-Server.git
branch = master
[submodule "third-party/TPCircularBuffer"]
path = third-party/TPCircularBuffer
@@ -60,9 +60,9 @@
branch = master
[submodule "third-party/wayland-protocols"]
path = third-party/wayland-protocols
url = https://github.com/LizardByte-infrastructure/wayland-protocols.git
url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git
branch = main
[submodule "third-party/wlr-protocols"]
path = third-party/wlr-protocols
url = https://github.com/LizardByte-infrastructure/wlr-protocols.git
url = https://gitlab.freedesktop.org/wlroots/wlr-protocols.git
branch = master

View File

@@ -5,18 +5,42 @@
</div>
<div align="center">
<a href="https://github.com/LizardByte/Sunshine"><img src="https://img.shields.io/github/stars/lizardbyte/sunshine.svg?logo=github&style=for-the-badge" alt="GitHub stars"></a>
<a href="https://github.com/LizardByte/Sunshine/releases/latest"><img src="https://img.shields.io/github/downloads/lizardbyte/sunshine/total.svg?style=for-the-badge&logo=github" alt="GitHub Releases"></a>
<a href="https://hub.docker.com/r/lizardbyte/sunshine"><img src="https://img.shields.io/docker/pulls/lizardbyte/sunshine.svg?style=for-the-badge&logo=docker" alt="Docker"></a>
<a href="https://github.com/LizardByte/Sunshine/pkgs/container/sunshine"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fipitio.github.io%2Fbackage%2FLizardByte%2FSunshine%2Fsunshine.json&query=%24.downloads&label=ghcr%20pulls&style=for-the-badge&logo=github" alt="GHCR"></a>
<a href="https://flathub.org/apps/dev.lizardbyte.app.Sunshine"><img src="https://img.shields.io/flathub/downloads/dev.lizardbyte.app.Sunshine?style=for-the-badge&logo=flathub" alt="Flathub installs"></a>
<a href="https://flathub.org/apps/dev.lizardbyte.app.Sunshine"><img src="https://img.shields.io/flathub/v/dev.lizardbyte.app.Sunshine?style=for-the-badge&logo=flathub" alt="Flathub Version"></a>
<a href="https://github.com/microsoft/winget-pkgs/tree/master/manifests/l/LizardByte/Sunshine"><img src="https://img.shields.io/winget/v/LizardByte.Sunshine?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHuSURBVFhH7ZfNTtRQGIYZiMDwN/IrCAqIhMSNKxcmymVwG+5dcDVsWHgDrtxwCYQVl+BChzDEwSnPY+eQ0sxoOz1mQuBNnpyvTdvz9jun5/SrjfxnJUkyQbMEz2ELduF1l0YUA3QyTrMAa2AnPtyOXsELeAYNyKtV2EC3k3lYgTOwg09ghy/BTp7CKBRV844BOpmmMV2+ySb4BmInG7AKY7AHH+EYqqhZo9PPBG/BVDlOizAD/XQFmnoPXzxRQX8M/CCYS48L6RIc4ygGHK9WGg9HZSZMUNRPVwNJGg5Hg2Qgqh4N3FsDsb6EmgYm07iwwvUxstdxJTwgmILf4CfZ6bb5OHANX8GN5x20IVxnG8ge94pt2xpwU3GnCwayF4Q2G2vgFLzHndFzQdk4q77nNfCdwL28qNyMtmEf3A1/QV5FjDiPWo5jrwf8TWZChTlgJvL4F9QL50/A43qVidTvLcuoM2wDQ1+IkgefgUpLcYwMVBqCKNJA2b0gKNocOIITOIef8C/F/CdMbh/GklynsSawKLHS8d9/B1x2LUqsfFyy3TMsWj5A1cLkotDbYO4JjWWZlZEGv8EbOIR1CAVN2eG8W5oNKgxaeC6DmTJjZs7ixUxpznLPLT+v4sXpoMLcLI3mzFSonDXIEI/M3QCIO4YuimBJ/gAAAABJRU5ErkJggg==" alt="Winget Version"></a>
<a href="https://gurubase.io/g/sunshine"><img src="https://img.shields.io/badge/Gurubase-Ask%20Guru-ef1a1b?style=for-the-badge&logo=data:image/jpeg;base64,/9j/2wCEAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDIBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIABgAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AOLqSO3mlilljido4QGkYDIQEgAn05IH41seFo7aS+uRKlrJci2Y2cd2QImlyOGyQPu7sA8ZxXapAlvpThbPRkv7nTQWhDoIZZRc/XaSAOmcZGOnFfP06XMr3P17F5iqE+Tl1uuvf9Lde55dRW74pit4r61EcdtFdG2U3kVqQY0lyeBgkD5duQOASawqykuV2O6jV9rTU0rXLNjf3Om3QubSXy5QCudoYEEYIIOQR7GnahqV3qk6zXk3mOqhFAUKqqOyqAAByeAKqUUXdrFezhz89lfv1+8KKKKRZ//Z" alt="Gurubase"></a>
<a href="https://github.com/LizardByte/Sunshine/actions/workflows/CI.yml?query=branch%3Amaster"><img src="https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/CI.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge" alt="GitHub Workflow Status (CI)"></a>
<a href="https://github.com/LizardByte/Sunshine/actions/workflows/localize.yml?query=branch%3Amaster"><img src="https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/localize.yml.svg?branch=master&label=localize%20build&logo=github&style=for-the-badge" alt="GitHub Workflow Status (localize)"></a>
<a href="https://docs.lizardbyte.dev/projects/sunshine"><img src="https://img.shields.io/readthedocs/sunshinestream.svg?label=Docs&style=for-the-badge&logo=readthedocs" alt="Read the Docs"></a>
<a href="https://codecov.io/gh/LizardByte/Sunshine"><img src="https://img.shields.io/codecov/c/gh/LizardByte/Sunshine?token=SMGXQ5NVMJ&style=for-the-badge&logo=codecov&label=codecov" alt="Codecov"></a>
<a href="https://github.com/LizardByte/Sunshine">
<img src="https://img.shields.io/github/stars/lizardbyte/sunshine.svg?logo=github&style=for-the-badge" alt="GitHub stars">
</a>
<a href="https://github.com/LizardByte/Sunshine/releases/latest">
<img src="https://img.shields.io/github/downloads/lizardbyte/sunshine/total.svg?style=for-the-badge&logo=github" alt="GitHub Releases">
</a>
<a href="https://hub.docker.com/r/lizardbyte/sunshine">
<img src="https://img.shields.io/docker/pulls/lizardbyte/sunshine.svg?style=for-the-badge&logo=docker" alt="Docker">
</a>
<a href="https://github.com/LizardByte/Sunshine/pkgs/container/sunshine">
<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fipitio.github.io%2Fbackage%2FLizardByte%2FSunshine%2Fsunshine.json&query=%24.downloads&label=ghcr%20pulls&style=for-the-badge&logo=github" alt="GHCR">
</a>
<a href="https://flathub.org/apps/dev.lizardbyte.app.Sunshine">
<img src="https://img.shields.io/flathub/downloads/dev.lizardbyte.app.Sunshine?style=for-the-badge&logo=flathub" alt="Flathub installs">
</a>
<a href="https://flathub.org/apps/dev.lizardbyte.app.Sunshine">
<img src="https://img.shields.io/flathub/v/dev.lizardbyte.app.Sunshine?style=for-the-badge&logo=flathub" alt="Flathub Version">
</a>
<a href="https://github.com/microsoft/winget-pkgs/tree/master/manifests/l/LizardByte/Sunshine">
<img src="https://img.shields.io/winget/v/LizardByte.Sunshine?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHuSURBVFhH7ZfNTtRQGIYZiMDwN/IrCAqIhMSNKxcmymVwG+5dcDVsWHgDrtxwCYQVl+BChzDEwSnPY+eQ0sxoOz1mQuBNnpyvTdvz9jun5/SrjfxnJUkyQbMEz2ELduF1l0YUA3QyTrMAa2AnPtyOXsELeAYNyKtV2EC3k3lYgTOwg09ghy/BTp7CKBRV844BOpmmMV2+ySb4BmInG7AKY7AHH+EYqqhZo9PPBG/BVDlOizAD/XQFmnoPXzxRQX8M/CCYS48L6RIc4ygGHK9WGg9HZSZMUNRPVwNJGg5Hg2Qgqh4N3FsDsb6EmgYm07iwwvUxstdxJTwgmILf4CfZ6bb5OHANX8GN5x20IVxnG8ge94pt2xpwU3GnCwayF4Q2G2vgFLzHndFzQdk4q77nNfCdwL28qNyMtmEf3A1/QV5FjDiPWo5jrwf8TWZChTlgJvL4F9QL50/A43qVidTvLcuoM2wDQ1+IkgefgUpLcYwMVBqCKNJA2b0gKNocOIITOIef8C/F/CdMbh/GklynsSawKLHS8d9/B1x2LUqsfFyy3TMsWj5A1cLkotDbYO4JjWWZlZEGv8EbOIR1CAVN2eG8W5oNKgxaeC6DmTJjZs7ixUxpznLPLT+v4sXpoMLcLI3mzFSonDXIEI/M3QCIO4YuimBJ/gAAAABJRU5ErkJggg==" alt="Winget Version">
</a>
<a href="https://gurubase.io/g/sunshine">
<img src="https://img.shields.io/badge/Gurubase-Ask%20Guru-ef1a1b?style=for-the-badge&logo=data:image/jpeg;base64,/9j/2wCEAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDIBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIABgAGAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AOLqSO3mlilljido4QGkYDIQEgAn05IH41seFo7aS+uRKlrJci2Y2cd2QImlyOGyQPu7sA8ZxXapAlvpThbPRkv7nTQWhDoIZZRc/XaSAOmcZGOnFfP06XMr3P17F5iqE+Tl1uuvf9Lde55dRW74pit4r61EcdtFdG2U3kVqQY0lyeBgkD5duQOASawqykuV2O6jV9rTU0rXLNjf3Om3QubSXy5QCudoYEEYIIOQR7GnahqV3qk6zXk3mOqhFAUKqqOyqAAByeAKqUUXdrFezhz89lfv1+8KKKKRZ//Z" alt="Gurubase">
</a>
<a href="https://github.com/LizardByte/Sunshine/actions/workflows/CI.yml?query=branch%3Amaster">
<img src="https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/CI.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge" alt="GitHub Workflow Status (CI)">
</a>
<a href="https://github.com/LizardByte/Sunshine/actions/workflows/localize.yml?query=branch%3Amaster">
<img src="https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/localize.yml.svg?branch=master&label=localize%20build&logo=github&style=for-the-badge" alt="GitHub Workflow Status (localize)">
</a>
<a href="https://docs.lizardbyte.dev/projects/sunshine">
<img src="https://img.shields.io/readthedocs/sunshinestream.svg?label=Docs&style=for-the-badge&logo=readthedocs" alt="Read the Docs">
</a>
<a href="https://codecov.io/gh/LizardByte/Sunshine">
<img src="https://img.shields.io/codecov/c/gh/LizardByte/Sunshine?token=SMGXQ5NVMJ&style=for-the-badge&logo=codecov&label=codecov" alt="Codecov">
</a>
</div>
## About
@@ -164,34 +188,6 @@ LizardByte has the full documentation hosted on [Read the Docs](https://docs.liz
Our support methods are listed in our [LizardByte Docs](https://docs.lizardbyte.dev/latest/about/support.html).
## 💲 Sponsors and Supporters
<p align="center">
<a href="https://app.lizardbyte.dev" aria-label="Sponsor LizardByte">
<img src='https://raw.githubusercontent.com/LizardByte/contributors/refs/heads/dist/sponsors.svg'/>
</a>
</p>
## 👥 Contributors
Thank you to all the contributors who have helped make Sunshine better!
### GitHub
<p align="center">
<a href="https://github.com/LizardByte/Sunshine" aria-label="GitHub">
<img src='https://raw.githubusercontent.com/LizardByte/contributors/refs/heads/dist/github.Sunshine.svg'/>
</a>
</p>
### CrowdIn
<p align="center">
<a href="https://translate.lizardbyte.dev" aria-label="CrowdIn">
<img src='https://raw.githubusercontent.com/LizardByte/contributors/refs/heads/dist/crowdin.svg'/>
</a>
</p>
<div class="section_buttons">
| Previous | Next |

View File

@@ -0,0 +1,17 @@
---
description: Bad Request - A parameter was not specified, or was specified incorrectly.
content:
application/json:
schema:
type: object
properties:
status_code:
type: string
status:
type: string
error:
type: string
example:
status_code: 400
status: false
error: "Bad Request"

View File

@@ -0,0 +1,17 @@
---
description: Unauthorized - The request requires user authentication.
content:
application/json:
schema:
type: object
properties:
status_code:
type: string
status:
type: string
error:
type: string
example:
status_code: 401
status: false
error: "Unauthorized"

View File

@@ -0,0 +1,7 @@
---
description: Forbidden - The server understood the request, but is refusing to fulfill it.
content:
# TODO: return JSON response.
text/plain:
schema:
type: string

View File

@@ -0,0 +1,15 @@
---
description: Not Found - The requested resource could not be found.
content:
application/json:
schema:
type: object
properties:
status_code:
type: integer
format: int32
error:
type: string
example:
status_code: 404
error: "Not Found"

View File

@@ -0,0 +1,48 @@
---
type: object
required:
- name
properties:
name:
type: string
description: Application Name, as shown on Moonlight
output:
type: string
description: The file where the output of the command is stored, if it is not specified, the output is ignored
cmd:
$ref: "./cmd.yml"
description: The main application to start. If blank, no application will be started.
exclude-global-prep-cmd:
type: boolean
description: Enable/Disable the execution of Global Prep Commands for this application.
elevated:
type: boolean
description: Run the application as an elevated process.
auto-detach:
type: boolean
description: Continue streaming if the application exits quickly
wait-all:
type: boolean
description: Continue streaming until all app processes exit
exit-timeout:
type: integer
description: Number of seconds to wait for all app processes to gracefully exit when requested to quit.
image-path:
type: string
description: |
Application icon/picture/image path that will be sent to client. Image must be a PNG file.
If not set, Sunshine will send default box image.
working-dir:
type: string
description: |
The working directory that should be passed to the process.
For example, some applications use the working directory to search for configuration files.
If not set, Sunshine will default to the parent directory of the command
prep-cmd:
type: array
items:
$ref: "./prep-cmd.yml"
detached:
type: array
items:
$ref: "./cmd.yml"

View File

@@ -0,0 +1,3 @@
---
type: string
description: Command to execute

View File

@@ -0,0 +1,16 @@
---
type: object
required:
- do
- undo
- elevated
properties:
do:
$ref: "./cmd.yml"
description: Command to run before the application starts.
undo:
$ref: "./cmd.yml"
description: Command to run after the application exits.
elevated:
type: boolean
description: Run the command as an elevated process.

44
api/openapi.yml Normal file
View File

@@ -0,0 +1,44 @@
---
# https://openapi.tools
openapi: 3.1.0
info:
title: Sunshine
summary: Self-hosted game stream host for Moonlight.
version: 0.0.0
contact:
name: LizardByte
url: https://app.lizardbyte.dev/support
license:
name: GNU General Public License v3.0 only
url: https://github.com/LizardByte/Sunshine/blob/master/LICENSE
servers:
- url: "https://{host}:{ui-port}"
description: Sunshine server
variables:
host:
default: "localhost"
ui-port:
default: 47990
security:
- basicAuth: []
components:
securitySchemes:
# TODO: update when JWT is implemented (https://github.com/LizardByte/Sunshine/pull/2995)
# https://swagger.io/specification/#security-scheme-object-examples
basicAuth:
description: HTTP Basic authentication
type: http
scheme: basic
paths:
/api/apps:
$ref: "./paths/confighttp/apps/apps.yml"
/api/apps/{index}:
$ref: "./paths/confighttp/apps/apps-by-index.yml"
/api/logs:
$ref: "./paths/confighttp/logs/logs.yml"

View File

@@ -0,0 +1,37 @@
---
delete:
summary: Delete an application.
description: |
Delete an application.
operationId: deleteApps
tags:
- Apps
parameters:
- name: index
in: path
description: The index of the application to delete.
required: true
schema:
type: integer
format: int32
responses:
'200':
description: The application was deleted successfully.
content:
application/json:
schema:
type: object
properties:
status:
type: string
result:
type: string
example:
status: true
result: "application 9999 deleted"
'400':
$ref: "../../../components/responses/400.yml"
'401':
$ref: "../../../components/responses/401.yml"
'403':
$ref: "../../../components/responses/403.yml"

View File

@@ -0,0 +1,151 @@
---
get:
summary: Get the list of available applications.
description: |
Get the list of available applications.
operationId: getApps
tags:
- Apps
responses:
'200':
description: A list of available applications.
content:
application/json:
schema:
type: array
items:
$ref: "../../../components/schemas/app.yml"
example:
- name: "Example App"
output: "/path/to/output.log"
cmd: "example-command"
exclude-global-prep-cmd: false
elevated: false
auto-detach: true
wait-all: false
exit-timeout: 30
image-path: "/path/to/image.png"
working-dir: "/path/to/working-dir"
prep-cmd:
- do: "prep-command-1"
undo: "undo-command-1"
elevated: false
detached:
- "detached-command-1"
'401':
$ref: "../../../components/responses/401.yml"
'403':
$ref: "../../../components/responses/403.yml"
post:
summary: Save an application.
description: |
Save an application.
To save a new application the index must be `-1`.
To update an existing application, you must provide the current index of the application.
operationId: postApps
tags:
- Apps
parameters:
- name: index
in: query
description: The index of the application to update. If the index is -1, a new application will be created.
required: true
schema:
type: integer
format: int32
- name: name
in: query
description: Application Name
required: false
schema:
type: string
- name: output
in: query
description: Log Output Path
required: false
schema:
type: string
- name: cmd
in: query
description: Command to run the application
required: false
schema:
$ref: "../../../components/schemas/cmd.yml"
- name: exclude-global-prep-cmd
in: query
description: Enable/Disable the execution of Global Prep Commands for this application.
required: false
schema:
type: boolean
- name: elevated
in: query
description: Run the application as an elevated process.
required: false
schema:
type: boolean
- name: auto-detach
in: query
description: Continue streaming if the application exits quickly
required: false
schema:
type: boolean
- name: wait-all
in: query
description: Continue streaming until all app processes exit
required: false
schema:
type: boolean
- name: exit-timeout
in: query
description: Number of seconds to wait for all app processes to gracefully exit when requested to quit.
required: false
schema:
type: integer
format: int32
- name: prep-cmd
in: query
description: Commands to run before the main application
required: false
schema:
type: array
items:
$ref: "../../../components/schemas/prep-cmd.yml"
- name: detached
in: query
description: Commands to run in detached processes
required: false
schema:
type: array
items:
$ref: "../../../components/schemas/cmd.yml"
- name: image-path
in: query
description: Full path to the application image. Must be a png file.
required: false
schema:
type: string
- name: working-dir
in: query
description: The working directory that should be passed to the process.
required: false
schema:
type: string
responses:
'200':
description: The application was saved successfully.
content:
application/json:
schema:
type: object
properties:
status:
type: string
example:
status: true
'400':
$ref: "../../../components/responses/400.yml"
'401':
$ref: "../../../components/responses/401.yml"
'403':
$ref: "../../../components/responses/403.yml"

View File

@@ -0,0 +1,20 @@
---
get:
summary: Get the logs from the log file.
description: |
Get the logs from the log file.
operationId: getLogs
tags:
- Logs
responses:
'200':
description: The contents of the log file.
content:
text/plain:
schema:
type: string
example: '[2025-01-15 17:07:58.131]: Info: Sunshine version: v...'
'401':
$ref: "../../../components/responses/401.yml"
'403':
$ref: "../../../components/responses/403.yml"

View File

@@ -20,9 +20,6 @@ install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/service/"
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/migration/"
DESTINATION "scripts"
COMPONENT assets)
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/path/"
DESTINATION "scripts"
COMPONENT assets)
# Configurable options for the service
install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/autostart/"
@@ -67,7 +64,6 @@ SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS
IfSilent +2 0
ExecShell 'open' 'https://docs.lizardbyte.dev/projects/sunshine'
nsExec::ExecToLog 'icacls \\\"$INSTDIR\\\" /reset'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\update-path.bat\\\" add'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\migrate-config.bat\\\"'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\add-firewall-rule.bat\\\"'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\install-gamepad.bat\\\"'
@@ -82,7 +78,7 @@ set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS
"${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS}
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\delete-firewall-rule.bat\\\"'
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\uninstall-service.bat\\\"'
nsExec::ExecToLog '\\\"$INSTDIR\\\\${CMAKE_PROJECT_NAME}.exe\\\" --restore-nvprefs-undo'
nsExec::ExecToLog '\\\"$INSTDIR\\\\sunshine.exe\\\" --restore-nvprefs-undo'
MessageBox MB_YESNO|MB_ICONQUESTION \
'Do you want to remove Virtual Gamepad?' \
/SD IDNO IDNO NoGamepad
@@ -92,18 +88,16 @@ set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS
'Do you want to remove $INSTDIR (this includes the configuration, cover images, and settings)?' \
/SD IDNO IDNO NoDelete
RMDir /r \\\"$INSTDIR\\\"; skipped if no
nsExec::ExecToLog '\\\"$INSTDIR\\\\scripts\\\\update-path.bat\\\" remove'
NoDelete:
")
# Adding an option for the start menu
set(CPACK_NSIS_MODIFY_PATH OFF)
set(CPACK_NSIS_MODIFY_PATH "OFF")
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
# This will be shown on the installed apps Windows settings
set(CPACK_NSIS_INSTALLED_ICON_NAME "${CMAKE_PROJECT_NAME}.exe")
set(CPACK_NSIS_CREATE_ICONS_EXTRA
"${CPACK_NSIS_CREATE_ICONS_EXTRA}
SetOutPath '\$INSTDIR'
CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\${CMAKE_PROJECT_NAME}.lnk' \
'\$INSTDIR\\\\${CMAKE_PROJECT_NAME}.exe' '--shortcut'
")

View File

@@ -974,9 +974,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}
disabled
@endcode</td>
<td colspan="2">@code{}verify_only@endcode</td>
</tr>
<tr>
<td>Example</td>
@@ -1156,15 +1154,14 @@ editing the `conf` file in a text editor. Use the examples as reference.
</tr>
</table>
### dd_wa_hdr_toggle_delay
### dd_wa_hdr_toggle
<table>
<tr>
<td>Description</td>
<td colspan="2">
When using virtual display device (VDD) for streaming, it might incorrectly display HDR color. Sunshine can try to mitigate this issue, by turning HDR off and then on again.<br>
If the value is set to 0, the workaround is disabled (default). If the value is between 0 and 3000 milliseconds, Sunshine will turn off HDR, wait for the specified amount of time and then turn HDR on again. The recommended delay time is around 500 milliseconds in most cases.<br>
DO NOT use this workaround unless you actually have issues with HDR as it directly impacts stream start time!
When using virtual display device as for streaming, it might display incorrect (high-contrast) color.
With this option enabled, Sunshine will try to mitigate this issue.
@note{This option works independently of [dd_hdr_option](#dd_hdr_option)}
@note{Applies to Windows only.}
</td>
@@ -1172,13 +1169,13 @@ editing the `conf` file in a text editor. Use the examples as reference.
<tr>
<td>Default</td>
<td colspan="2">@code{}
0
disabled
@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
dd_wa_hdr_toggle_delay = 500
dd_wa_hdr_toggle = enabled
@endcode</td>
</tr>
</table>
@@ -1317,29 +1314,6 @@ editing the `conf` file in a text editor. Use the examples as reference.
</tr>
</table>
### max_bitrate
<table>
<tr>
<td>Description</td>
<td colspan="2">
The maximum bitrate (in Kbps) that Sunshine will encode the stream at. If set to 0, it will always use the bitrate requested by Moonlight.
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}
0
@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
max_bitrate = 5000
@endcode</td>
</tr>
</table>
### min_fps_factor
<table>

View File

@@ -2,12 +2,6 @@
Read our contribution guide in our organization level
[docs](https://docs.lizardbyte.dev/latest/developers/contributing.html).
## Recommended Tools
| Tool | Description |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <a href="https://www.jetbrains.com/clion/"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/CLion_icon.svg" width="30" height="30"></a><br>CLion | Recommended IDE for C++ development. Free licenses available for open source developers through the [JetBrains Open Source Program](https://www.jetbrains.com/community/opensource/). |
## Project Patterns
### Web UI

View File

@@ -335,6 +335,8 @@ recommended for most users. No support will be provided!}
scripts/uninstall-service.bat
```
To uninstall, delete the extracted directory which contains the `sunshine.exe` file.
## Initial Setup
After installation, some initial setup is required.

View File

@@ -118,16 +118,6 @@ system. You may also want to enable decoders, however that is not required for S
```
}
### Input not working
After installation, the `udev` rules need to be reloaded. Our post-install script tries to do this for you
automatically, but if it fails you may need to restart your system.
If the input is still not working, you may need to add your user to the `input` group.
```bash
sudo usermod -aG input $USER
```
@note{Other build options are listed in the
[meson options](https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/meson_options.txt) file.}

View File

@@ -1,25 +0,0 @@
---
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
version: 2
build:
os: ubuntu-24.04
tools:
ruby: "3.3"
apt_packages:
- 7zip
- jq
jobs:
install:
- |
mkdir -p "./tmp"
branch="master"
base_url="https://raw.githubusercontent.com/LizardByte/LizardByte.github.io"
url="${base_url}/refs/heads/${branch}/scripts/readthedocs_build.sh"
curl -sSL -o "./tmp/readthedocs_build.sh" "${url}"
chmod +x "./tmp/readthedocs_build.sh"
build:
html:
- "./tmp/readthedocs_build.sh"

View File

@@ -538,11 +538,11 @@ ext-js:
<div class="card-footer p-3 px-4">
<a class="latest-button btn btn-outline-light me-3 mb-3 d-none" href="https://github.com/LizardByte/Sunshine/releases/latest" target="_blank">
<i class="fa-fw fab fa-github"></i>
Latest: <span id="latest-version" class="crowdin-ignore"></span>
Latest: <span id="latest-version"></span>
</a>
<a class="beta-button btn btn-outline-light me-3 mb-3 d-none" href="#" target="_blank">
<i class="fa-fw fas fa-flask"></i>
Beta: <span id="beta-version" class="crowdin-ignore"></span>
Beta: <span id="beta-version"></span>
</a>
<a class="btn btn-outline-light me-3 mb-3" href="https://github.com/LizardByte/pacman-repo" target="_blank">
<i class="fa-fw fab fa-linux"></i>
@@ -577,21 +577,16 @@ ext-js:
// Filter the releases to get only the stable releases
const stableReleases = data.filter(release => !release.prerelease);
const latestButton = document.querySelector('.latest-button');
const latestVersion = document.querySelector('#latest-version');
const betaButton = document.querySelector('.beta-button');
const betaVersion = document.querySelector('#beta-version');
// If there are no stable releases, hide the latest download button
if (stableReleases.length === 0) {
latestButton.classList.add('d-none');
document.querySelector('.latest-button').classList.add('d-none');
} else {
// Show the latest download button
latestButton.classList.remove('d-none');
document.querySelector('.latest-button').classList.remove('d-none');
// Get the latest stable release
const latestStableRelease = stableReleases[0];
latestVersion.textContent = latestStableRelease.tag_name;
document.querySelector('#latest-version').textContent = latestStableRelease.tag_name;
// If there is a pre-release, update the href attribute of the anchor tag
if (preReleases.length > 0) {
@@ -603,16 +598,16 @@ ext-js:
// If the pre-release is newer, update the href attribute of the anchor tag
if (preReleaseDate > stableReleaseDate) {
betaButton.href = latestPreRelease.html_url;
betaVersion.textContent = latestPreRelease.tag_name;
betaButton.classList.remove('d-none');
document.querySelector('.beta-button').href = latestPreRelease.html_url;
document.querySelector('#beta-version').textContent = latestPreRelease.tag_name;
document.querySelector('.beta-button').classList.remove('d-none');
} else {
// If the pre-release is older, hide the button
betaButton.classList.add('d-none');
document.querySelector('.beta-button').classList.add('d-none');
}
} else {
// If there is no pre-release, hide the button
betaButton.classList.add('d-none');
document.querySelector('.beta-button').classList.add('d-none');
}
}
});

View File

@@ -8,14 +8,14 @@
"serve": "serve ./tests/fixtures/http --no-port-switching"
},
"dependencies": {
"@lizardbyte/shared-web": "2025.326.11214",
"@lizardbyte/shared-web": "2024.921.191855",
"vue": "3.5.13",
"vue-i18n": "11.1.2"
"vue-i18n": "11.0.1"
},
"devDependencies": {
"@vitejs/plugin-vue": "4.6.2",
"serve": "14.2.3",
"vite": "4.5.9",
"vite": "4.5.2",
"vite-plugin-ejs": "1.6.4"
}
}

View File

@@ -1,22 +1,13 @@
<div align="center">
<img src="https://raw.githubusercontent.com/LizardByte/Sunshine/master/sunshine.png" />
<h1 align="center">Sunshine</h1>
<h4 align="center">Self-hosted game stream host for Moonlight.</h4>
</div>
# Overview
<div align="center">
<a href="https://flathub.org/apps/dev.lizardbyte.app.Sunshine"><img src="https://img.shields.io/flathub/downloads/dev.lizardbyte.app.Sunshine?style=for-the-badge&logo=flathub" alt="Flathub installs"></a>
<a href="https://flathub.org/apps/dev.lizardbyte.app.Sunshine"><img src="https://img.shields.io/flathub/v/dev.lizardbyte.app.Sunshine?style=for-the-badge&logo=flathub" alt="Flathub Version"></a>
</div>
[![Flathub installs](https://img.shields.io/flathub/downloads/dev.lizardbyte.app.Sunshine?style=for-the-badge&logo=flathub)](https://flathub.org/apps/dev.lizardbyte.app.Sunshine)
[![Flathub Version](https://img.shields.io/flathub/v/dev.lizardbyte.app.Sunshine?style=for-the-badge&logo=flathub)](https://flathub.org/apps/dev.lizardbyte.app.Sunshine)
## About
LizardByte has the full documentation hosted on [Read the Docs](https://docs.lizardbyte.dev/projects/sunshine).
## About
Sunshine is a self-hosted game stream host for Moonlight.
LizardByte has the full documentation hosted on [Read the Docs](https://docs.lizardbyte.dev/projects/sunshine)
* [Stable](https://docs.lizardbyte.dev/projects/sunshine/latest/)
* [Beta](https://docs.lizardbyte.dev/projects/sunshine/master/)
This repo is synced from the upstream [Sunshine](https://github.com/LizardByte/Sunshine) repo.
Please report issues and contribute to the upstream repo.

View File

@@ -1,8 +0,0 @@
{
"dev.lizardbyte.app.Sunshine": [
"appstream-missing-screenshots",
"appstream-screenshots-not-mirrored-in-ostree",
"external-gitmodule-url-found",
"finish-args-flatpak-spawn-access"
]
}

View File

@@ -0,0 +1,9 @@
{
"errors": [
"finish-args-flatpak-spawn-access"
],
"info": [
"finish-args-flatpak-spawn-access: finish-args has a talk-name access for org.freedesktop.Flatpak"
],
"message": "Please consult the documentation at https://docs.flathub.org/docs/for-app-authors/linter"
}

View File

@@ -0,0 +1,11 @@
{
"errors": [
"appstream-missing-screenshots",
"finish-args-flatpak-spawn-access"
],
"info": [
"appstream-missing-screenshots: Catalogue file has no screenshots. Please check if screenshot URLs are reachable",
"finish-args-flatpak-spawn-access: finish-args has a talk-name access for org.freedesktop.Flatpak"
],
"message": "Please consult the documentation at https://docs.flathub.org/docs/for-app-authors/linter"
}

View File

@@ -11,15 +11,14 @@
],
"sources": [
{
"type": "git",
"url": "https://github.com/LizardByte-infrastructure/xserver.git",
"tag": "xorg-server-21.1.13",
"commit": "be2767845d6ed3c6dbd25a151051294d0908a995",
"type": "archive",
"url": "https://gitlab.freedesktop.org/xorg/xserver/-/archive/xorg-server-21.1.13/xserver-xorg-server-21.1.13.tar.bz2",
"sha256": "ee2bf6d65f4b111ce86ca817c3327dc1e70d9c958aa16876f2820caf7bf7cffa",
"x-checker-data": {
"type": "anitya",
"project-id": 5250,
"stable-only": true,
"tag-template": "xorg-server-$version"
"url-template": "https://gitlab.freedesktop.org/xorg/xserver/-/archive/xorg-server-$version/xserver-xorg-server-$version.tar.bz2"
}
},
{
@@ -33,15 +32,14 @@
"buildsystem": "meson",
"sources": [
{
"type": "git",
"url": "https://github.com/LizardByte-infrastructure/libxcvt.git",
"tag": "libxcvt-0.1.2",
"commit": "d9ca87eea9eecddaccc3a77227bcb3acf84e89df",
"type": "archive",
"url": "https://gitlab.freedesktop.org/xorg/lib/libxcvt/-/archive/libxcvt-0.1.2/libxcvt-libxcvt-0.1.2.tar.bz2",
"sha256": "590e5a6da87ace7aa7857026b207a2c4d378620035441e20ea97efedd15d6d4a",
"x-checker-data": {
"type": "anitya",
"project-id": 235147,
"stable-only": true,
"tag-template": "libxcvt-$version"
"url-template": "https://gitlab.freedesktop.org/xorg/lib/libxcvt/-/archive/libxcvt-$version/libxcvt-libxcvt-$version.tar.bz2"
}
}
]
@@ -50,32 +48,14 @@
"name": "libXmu",
"sources": [
{
"type": "git",
"url": "https://github.com/LizardByte-infrastructure/libxmu.git",
"tag": "libXmu-1.2.1",
"commit": "792f80402ee06ce69bca3a8f2a84295999c3a170",
"type": "archive",
"url": "https://xorg.freedesktop.org/archive/individual/lib/libXmu-1.2.1.tar.gz",
"sha256": "bf0902583dd1123856c11e0a5085bd3c6e9886fbbd44954464975fd7d52eb599",
"x-checker-data": {
"type": "anitya",
"project-id": 1785,
"stable-only": true,
"tag-template": "libXmu-$version"
}
}
]
},
{
"name": "font-util",
"sources": [
{
"type": "git",
"url": "https://github.com/LizardByte-infrastructure/font-util.git",
"tag": "font-util-1.4.1",
"commit": "b5ca142f81a6f14eddb23be050291d1c25514777",
"x-checker-data": {
"type": "anitya",
"project-id": 15055,
"stable-only": true,
"tag-template": "font-util-$version"
"url-template": "https://xorg.freedesktop.org/archive/individual/lib/libXmu-$version.tar.gz"
}
}
]
@@ -84,15 +64,14 @@
"name": "libfontenc",
"sources": [
{
"type": "git",
"url": "https://github.com/LizardByte-infrastructure/libfontenc.git",
"tag": "libfontenc-1.1.8",
"commit": "92a85fda2acb4e14ec0b2f6d8fe3eaf2b687218c",
"type": "archive",
"url": "https://xorg.freedesktop.org/archive/individual/lib/libfontenc-1.1.8.tar.xz",
"sha256": "7b02c3d405236e0d86806b1de9d6868fe60c313628b38350b032914aa4fd14c6",
"x-checker-data": {
"type": "anitya",
"project-id": 1613,
"stable-only": true,
"tag-template": "libfontenc-$version"
"url-template": "https://xorg.freedesktop.org/archive/individual/lib/libfontenc-$version.tar.xz"
}
}
]
@@ -116,19 +95,34 @@
}
]
},
{
"name": "font-util",
"sources": [
{
"type": "archive",
"url": "https://xorg.freedesktop.org/archive/individual/font/font-util-1.4.1.tar.gz",
"sha256": "f029ae80cdd75d89bee7f7af61c21e07982adfb9f72344a158b99f91f77ef5ed",
"x-checker-data": {
"type": "anitya",
"project-id": 15055,
"stable-only": true,
"url-template": "https://xorg.freedesktop.org/archive/individual/font/font-util-$version.tar.gz"
}
}
]
},
{
"name": "xvfb-libXfont2",
"sources": [
{
"type": "git",
"url": "https://github.com/LizardByte-infrastructure/libxfont.git",
"tag": "libXfont2-2.0.6",
"commit": "d54aaf2483df6a1f98fadc09004157e657b7f73e",
"type": "archive",
"url": "https://xorg.freedesktop.org/archive/individual/lib/libXfont2-2.0.6.tar.gz",
"sha256": "a944df7b6837c8fa2067f6a5fc25d89b0acc4011cd0bc085106a03557fb502fc",
"x-checker-data": {
"type": "anitya",
"project-id": 17165,
"stable-only": true,
"tag-template": "libXfont2-$version"
"url-template": "https://xorg.freedesktop.org/archive/individual/lib/libXfont2-$version.tar.gz"
}
}
]
@@ -137,15 +131,14 @@
"name": "xvfb-xauth",
"sources": [
{
"type": "git",
"url": "https://github.com/LizardByte-infrastructure/xauth.git",
"tag": "xauth-1.1.3",
"commit": "c29eef23683f0e3575a3c60d9314de8156fbe2c2",
"type": "archive",
"url": "https://gitlab.freedesktop.org/xorg/app/xauth/-/archive/xauth-1.1.1/xauth-xauth-1.1.3.tar.bz2",
"sha256": "3cee16ebe9de0e85c62513f6d6353710407c8ebb1f855b18d03807c27d38a215",
"x-checker-data": {
"type": "anitya",
"project-id": 5253,
"stable-only": true,
"tag-template": "xauth-$version"
"url-template": "https://gitlab.freedesktop.org/xorg/app/xauth/-/archive/xauth-1.1.1/xauth-xauth-$version.tar.bz2"
}
}
]

View File

@@ -2,23 +2,12 @@
# User Service
mkdir -p ~/.config/systemd/user
cp /app/share/sunshine/systemd/user/sunshine.service "${HOME}/.config/systemd/user/sunshine.service"
cp /app/share/sunshine/systemd/user/sunshine.service $HOME/.config/systemd/user/sunshine.service
echo Sunshine User Service has been installed.
echo Use [systemctl --user enable sunshine] once to autostart Sunshine on login.
# Udev rule
UDEV=$(cat /app/share/sunshine/udev/rules.d/60-sunshine.rules)
echo Configuring input permissions.
echo Configuring mouse permission.
flatpak-spawn --host pkexec sh -c "echo '$UDEV' > /etc/udev/rules.d/60-sunshine.rules"
# Reload udev rules
path_to_udevadm=$(flatpak-spawn --host which udevadm)
if [ -x "$path_to_udevadm" ] ; then
echo "Reloading udev rules."
flatpak-spawn --host "$path_to_udevadm" control --reload-rules
flatpak-spawn --host "$path_to_udevadm" trigger --property-match=DEVNAME=/dev/uinput
flatpak-spawn --host "$path_to_udevadm" trigger --property-match=DEVNAME=/dev/uhid
echo "Udev rules reloadeded successfully."
else
echo "error: udevadm not found or not executable."
fi
echo Restart computer for mouse permission to take effect.

View File

@@ -2,22 +2,10 @@
# User Service
systemctl --user stop sunshine
rm "${HOME}/.config/systemd/user/sunshine.service"
rm $HOME/.config/systemd/user/sunshine.service
systemctl --user daemon-reload
echo Sunshine User Service has been removed.
# Udev rule
echo Removing input permissions.
flatpak-spawn --host pkexec sh -c "rm /etc/udev/rules.d/60-sunshine.rules"
# Reload udev rules
path_to_udevadm=$(flatpak-spawn --host which udevadm)
if [ -x "$path_to_udevadm" ] ; then
echo "Reloading udev rules."
flatpak-spawn --host "$path_to_udevadm" control --reload-rules
flatpak-spawn --host "$path_to_udevadm" trigger --property-match=DEVNAME=/dev/uinput
flatpak-spawn --host "$path_to_udevadm" trigger --property-match=DEVNAME=/dev/uhid
echo "Udev rules reloadeded successfully."
else
echo "error: udevadm not found or not executable."
fi
echo Mouse permission removed. Restart computer to take effect.

View File

@@ -3,7 +3,6 @@ set -e
# Default value for arguments
appimage_build=0
num_processors=$(nproc)
publisher_name="Third Party Publisher"
publisher_website=""
publisher_issue_url="https://app.lizardbyte.dev/support"
@@ -28,7 +27,6 @@ Options:
-h, --help Display this help message.
-s, --sudo-off Disable sudo command.
--appimage-build Compile for AppImage, this will not create the AppImage, just the executable.
--num-processors The number of processors to use for compilation. Default is the value of 'nproc'.
--publisher-name The name of the publisher (not developer) of the application.
--publisher-website The URL of the publisher's website.
--publisher-issue-url The URL of the publisher's support site or issue tracker.
@@ -55,9 +53,6 @@ while getopts ":hs-:" opt; do
appimage_build=1
skip_libva=1
;;
num-processors=*)
num_processors="${OPTARG#*=}"
;;
publisher-name=*)
publisher_name="${OPTARG#*=}"
;;
@@ -372,7 +367,7 @@ function run_install() {
tar -xzf "${build_dir}/doxygen.tar.gz"
cd "doxygen-${doxygen_min}"
cmake -DCMAKE_BUILD_TYPE=Release -G="Ninja" -B="build" -S="."
ninja -C "build" -j"${num_processors}"
ninja -C "build"
ninja -C "build" install
else
echo "Doxygen version too low, skipping docs"

View File

@@ -1,2 +1,2 @@
Babel==2.17.0
Babel==2.16.0
clang-format

View File

@@ -497,7 +497,7 @@ namespace config {
{}, // output_name
{
video_t::dd_t::config_option_e::disabled, // configuration_option
video_t::dd_t::config_option_e::verify_only, // configuration_option
video_t::dd_t::resolution_option_e::automatic, // resolution_option
{}, // manual_resolution
video_t::dd_t::refresh_rate_option_e::automatic, // refresh_rate_option
@@ -509,8 +509,7 @@ namespace config {
{} // wa
}, // display_device
1, // min_fps_factor
0 // max_bitrate
1 // min_fps_factor
};
audio_t audio {
@@ -1136,14 +1135,9 @@ namespace config {
}
bool_f(vars, "dd_config_revert_on_disconnect", video.dd.config_revert_on_disconnect);
generic_f(vars, "dd_mode_remapping", video.dd.mode_remapping, dd::mode_remapping_from_view);
{
int value = 0;
int_between_f(vars, "dd_wa_hdr_toggle_delay", value, {0, 3000});
video.dd.wa.hdr_toggle_delay = std::chrono::milliseconds {value};
}
bool_f(vars, "dd_wa_hdr_toggle", video.dd.wa.hdr_toggle);
int_between_f(vars, "min_fps_factor", video.min_fps_factor, {1, 3});
int_f(vars, "max_bitrate", video.max_bitrate);
path_f(vars, "pkey", nvhttp.pkey);
path_f(vars, "cert", nvhttp.cert);

View File

@@ -84,7 +84,7 @@ namespace config {
struct dd_t {
struct workarounds_t {
std::chrono::milliseconds hdr_toggle_delay; ///< Specify whether to apply HDR high-contrast color workaround and what delay to use.
bool hdr_toggle; ///< Specify whether to apply HDR high-contrast color workaround.
};
enum class config_option_e {
@@ -138,7 +138,6 @@ namespace config {
} dd;
int min_fps_factor; // Minimum fps target, determines minimum frame time
int max_bitrate; // Maximum bitrate, sets ceiling in kbps for bitrate requested from client
};
struct audio_t {

View File

@@ -621,7 +621,7 @@ namespace display_device {
std::make_shared<FileSettingsPersistence>(persistence_filepath)
),
WinWorkarounds {
.m_hdr_blank_delay = video_config.dd.wa.hdr_toggle_delay != std::chrono::milliseconds::zero() ? std::make_optional(video_config.dd.wa.hdr_toggle_delay) : std::nullopt
.m_hdr_blank_delay = video_config.dd.wa.hdr_toggle ? std::make_optional(500ms) : std::nullopt
}
);
#else

View File

@@ -23,7 +23,7 @@ namespace http {
);
int reload_user_creds(const std::string &file);
bool download_file(const std::string &url, const std::string &file, long ssl_version = CURL_SSLVERSION_TLSv1_2);
bool download_file(const std::string &url, const std::string &file, long ssl_version = CURL_SSLVERSION_TLSv1_3);
std::string url_escape(const std::string &url);
std::string url_get_host(const std::string &url);

View File

@@ -528,7 +528,7 @@ namespace nvenc {
NV_ENC_LOCK_BITSTREAM lock_bitstream = {min_struct_version(NV_ENC_LOCK_BITSTREAM_VER, 1, 2)};
lock_bitstream.outputBitstream = output_bitstream;
lock_bitstream.doNotWait = async_event_handle ? 1 : 0;
lock_bitstream.doNotWait = 0;
if (async_event_handle && !wait_for_async_event(100)) {
BOOST_LOG(error) << "NvEnc: frame " << frame_index << " encode wait timeout";

View File

@@ -10,19 +10,11 @@
namespace nvenc {
nvenc_d3d11::nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type):
nvenc_base(device_type) {
async_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL);
}
nvenc_d3d11::~nvenc_d3d11() {
if (dll) {
FreeLibrary(dll);
dll = NULL;
}
if (async_event_handle) {
CloseHandle(async_event_handle);
}
}
bool nvenc_d3d11::init_library() {
@@ -61,9 +53,5 @@ namespace nvenc {
return false;
}
bool nvenc_d3d11::wait_for_async_event(uint32_t timeout_ms) {
return WaitForSingleObject(async_event_handle, timeout_ms) == WAIT_OBJECT_0;
}
} // namespace nvenc
#endif

View File

@@ -25,7 +25,10 @@ namespace nvenc {
*/
class nvenc_d3d11: public nvenc_base {
public:
explicit nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type);
explicit nvenc_d3d11(NV_ENC_DEVICE_TYPE device_type):
nvenc_base(device_type) {
}
~nvenc_d3d11();
/**
@@ -36,7 +39,6 @@ namespace nvenc {
protected:
bool init_library() override;
bool wait_for_async_event(uint32_t timeout_ms) override;
private:
HMODULE dll = NULL;

View File

@@ -106,7 +106,6 @@ namespace platf {
rumble_triggers, ///< Rumble triggers
set_motion_event_state, ///< Set motion event state
set_rgb_led, ///< Set RGB LED
set_adaptive_triggers, ///< Set adaptive triggers
};
struct gamepad_feedback_msg_t {
@@ -143,14 +142,6 @@ namespace platf {
return msg;
}
static gamepad_feedback_msg_t make_adaptive_triggers(std::uint16_t id, uint8_t event_flags, uint8_t type_left, uint8_t type_right, const std::array<uint8_t, 10> &left, const std::array<uint8_t, 10> &right) {
gamepad_feedback_msg_t msg;
msg.type = gamepad_feedback_e::set_adaptive_triggers;
msg.id = id;
msg.data.adaptive_triggers = {.event_flags = event_flags, .type_left = type_left, .type_right = type_right, .left = left, .right = right};
return msg;
}
gamepad_feedback_e type;
std::uint16_t id;
@@ -175,15 +166,6 @@ namespace platf {
std::uint8_t g;
std::uint8_t b;
} rgb_led;
struct {
uint16_t controllerNumber;
uint8_t event_flags;
uint8_t type_left;
uint8_t type_right;
std::array<uint8_t, 10> left;
std::array<uint8_t, 10> right;
} adaptive_triggers;
} data;
};

View File

@@ -43,7 +43,7 @@ namespace platf::gamepad {
}
auto create_ds5() {
return inputtino::PS5Joypad::create({.name = "Sunshine PS5 (virtual) pad", .vendor_id = 0x054C, .product_id = 0x0CE6, .version = 0x8111});
return inputtino::PS5Joypad::create({.name = "Sunshine DualSense (virtual) pad", .vendor_id = 0x054C, .product_id = 0x0CE6, .version = 0x8111});
}
int alloc(input_raw_t *raw, const gamepad_id_t &id, const gamepad_arrival_t &metadata, feedback_queue_t feedback_queue) {
@@ -152,10 +152,6 @@ namespace platf::gamepad {
gamepad->last_rgb_led = msg;
});
(*ds5).set_on_trigger_effect([feedback_queue, idx = id.clientRelativeIndex](const inputtino::PS5Joypad::TriggerEffect &trigger_effect) {
feedback_queue->raise(gamepad_feedback_msg_t::make_adaptive_triggers(idx, trigger_effect.event_flags, trigger_effect.type_left, trigger_effect.type_right, trigger_effect.left, trigger_effect.right));
});
// Activate the motion sensors
feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_ACCEL, 100));
feedback_queue->raise(gamepad_feedback_msg_t::make_motion_event_state(id.clientRelativeIndex, LI_MOTION_TYPE_GYRO, 100));

View File

@@ -48,7 +48,6 @@ extern "C" {
#define IDX_RUMBLE_TRIGGER_DATA 12
#define IDX_SET_MOTION_EVENT 13
#define IDX_SET_RGB_LED 14
#define IDX_SET_ADAPTIVE_TRIGGERS 15
static const short packetTypes[] = {
0x0305, // Start A
@@ -66,7 +65,6 @@ static const short packetTypes[] = {
0x5500, // Rumble triggers (Sunshine protocol extension)
0x5501, // Set motion event (Sunshine protocol extension)
0x5502, // Set RGB LED (Sunshine protocol extension)
0x5503, // Set Adaptive triggers (Sunshine protocol extension)
};
namespace asio = boost::asio;
@@ -188,21 +186,6 @@ namespace stream {
std::uint8_t b;
};
struct control_adaptive_triggers_t {
control_header_v2 header;
std::uint16_t id;
/**
* 0x04 - Right trigger
* 0x08 - Left trigger
*/
std::uint8_t event_flags;
std::uint8_t type_left;
std::uint8_t type_right;
std::uint8_t left[DS_EFFECT_PAYLOAD_SIZE];
std::uint8_t right[DS_EFFECT_PAYLOAD_SIZE];
};
struct control_hdr_mode_t {
control_header_v2 header;
@@ -850,22 +833,6 @@ namespace stream {
plaintext.b = data.b;
BOOST_LOG(verbose) << "RGB: "sv << msg.id << " :: "sv << util::hex(data.r).to_string_view() << util::hex(data.g).to_string_view() << util::hex(data.b).to_string_view();
std::array<std::uint8_t, sizeof(control_encrypted_t) + crypto::cipher::round_to_pkcs7_padded(sizeof(plaintext)) + crypto::cipher::tag_size>
encrypted_payload;
payload = encode_control(session, util::view(plaintext), encrypted_payload);
} else if (msg.type == platf::gamepad_feedback_e::set_adaptive_triggers) {
control_adaptive_triggers_t plaintext;
plaintext.header.type = packetTypes[IDX_SET_ADAPTIVE_TRIGGERS];
plaintext.header.payloadLength = sizeof(plaintext) - sizeof(control_header_v2);
plaintext.id = util::endian::little(msg.id);
plaintext.event_flags = msg.data.adaptive_triggers.event_flags;
plaintext.type_left = msg.data.adaptive_triggers.type_left;
std::ranges::copy(msg.data.adaptive_triggers.left, plaintext.left);
plaintext.type_right = msg.data.adaptive_triggers.type_right;
std::ranges::copy(msg.data.adaptive_triggers.right, plaintext.right);
std::array<std::uint8_t, sizeof(control_encrypted_t) + crypto::cipher::round_to_pkcs7_padded(sizeof(plaintext)) + crypto::cipher::tag_size>
encrypted_payload;

View File

@@ -299,7 +299,6 @@ namespace video {
REF_FRAMES_INVALIDATION = 1 << 8, ///< Support reference frames invalidation
ALWAYS_REPROBE = 1 << 9, ///< This is an encoder of last resort and we want to aggressively probe for a better one
YUV444_SUPPORT = 1 << 10, ///< Encoder may support 4:4:4 chroma sampling depending on hardware
ASYNC_TEARDOWN = 1 << 11, ///< Encoder supports async teardown on a different thread
};
class avcodec_encode_session_t: public encode_session_t {
@@ -504,7 +503,7 @@ namespace video {
{}, // Fallback options
"h264_nvenc"s,
},
PARALLEL_ENCODING | REF_FRAMES_INVALIDATION | YUV444_SUPPORT | ASYNC_TEARDOWN // flags
PARALLEL_ENCODING | REF_FRAMES_INVALIDATION | YUV444_SUPPORT // flags
};
#elif !defined(__APPLE__)
encoder_t nvenc {
@@ -1687,8 +1686,7 @@ namespace video {
}
}
auto bitrate = ((config::video.max_bitrate > 0) ? std::min(config.bitrate, config::video.max_bitrate) : config.bitrate) * 1000;
BOOST_LOG(info) << "Streaming bitrate is " << bitrate;
auto bitrate = config.bitrate * 1000;
ctx->rc_max_rate = bitrate;
ctx->bit_rate = bitrate;
@@ -1858,23 +1856,6 @@ namespace video {
return;
}
// As a workaround for NVENC hangs and to generally speed up encoder reinit,
// we will complete the encoder teardown in a separate thread if supported.
// This will move expensive processing off the encoder thread to allow us
// to restart encoding as soon as possible. For cases where the NVENC driver
// hang occurs, this thread may probably never exit, but it will allow
// streaming to continue without requiring a full restart of Sunshine.
auto fail_guard = util::fail_guard([&encoder, &session] {
if (encoder.flags & ASYNC_TEARDOWN) {
std::thread encoder_teardown_thread {[session = std::move(session)]() mutable {
BOOST_LOG(info) << "Starting async encoder teardown";
session.reset();
BOOST_LOG(info) << "Async encoder teardown complete";
}};
encoder_teardown_thread.detach();
}
});
// set minimum frame time, avoiding violation of client-requested target framerate
auto minimum_frame_time = std::chrono::milliseconds(1000 / std::min(config.framerate, (config::video.min_fps_factor * 10)));
BOOST_LOG(debug) << "Minimum frame time set to "sv << minimum_frame_time.count() << "ms, based on min fps factor of "sv << config::video.min_fps_factor << "."sv;

View File

@@ -441,7 +441,7 @@
);
if (resp) {
fetch("./api/apps/" + id, { method: "DELETE" }).then((r) => {
if (r.status === 200) document.location.reload();
if (r.status == 200) document.location.reload();
});
}
},
@@ -557,7 +557,7 @@
method: "POST",
body: JSON.stringify(this.editForm),
}).then((r) => {
if (r.status === 200) document.location.reload();
if (r.status == 200) document.location.reload();
});
},
},

View File

@@ -168,7 +168,7 @@
"install_steam_audio_drivers": "enabled",
"adapter_name": "",
"output_name": "",
"dd_configuration_option": "disabled",
"dd_configuration_option": "verify_only",
"dd_resolution_option": "auto",
"dd_manual_resolution": "",
"dd_refresh_rate_option": "auto",
@@ -177,9 +177,8 @@
"dd_config_revert_delay": 3000,
"dd_config_revert_on_disconnect": "disabled",
"dd_mode_remapping": {"mixed": [], "resolution_only": [], "refresh_rate_only": []},
"dd_wa_hdr_toggle_delay": 0,
"dd_wa_hdr_toggle": "disabled",
"min_fps_factor": 1,
"max_bitrate": 0,
},
},
{

View File

@@ -1,6 +1,7 @@
<script setup>
import { ref } from 'vue'
import PlatformLayout from '../../../PlatformLayout.vue'
import Checkbox from "../../../Checkbox.vue";
const props = defineProps({
platform: String,
@@ -67,7 +68,7 @@ function addRemappingEntry() {
{{ $t('config.dd_config_label') }}
</label>
<select id="dd_configuration_option" class="form-select" v-model="config.dd_configuration_option">
<option value="disabled">{{ $t('_common.disabled_def') }}</option>
<option value="disabled">{{ $t('_common.disabled') }}</option>
<option value="verify_only">{{ $t('config.dd_config_verify_only') }}</option>
<option value="ensure_active">{{ $t('config.dd_config_ensure_active') }}</option>
<option value="ensure_primary">{{ $t('config.dd_config_ensure_primary') }}</option>
@@ -131,18 +132,11 @@ function addRemappingEntry() {
<option value="auto">{{ $t('config.dd_hdr_option_auto') }}</option>
</select>
<!-- HDR toggle -->
<label for="dd_wa_hdr_toggle_delay" class="form-label">
{{ $t('config.dd_wa_hdr_toggle_delay') }}
</label>
<input type="number" class="form-control" id="dd_wa_hdr_toggle_delay" placeholder="0" min="0" max="3000"
v-model="config.dd_wa_hdr_toggle_delay" />
<div class="form-text">
{{ $t('config.dd_wa_hdr_toggle_delay_desc_1') }}
<br>
{{ $t('config.dd_wa_hdr_toggle_delay_desc_2') }}
<br>
{{ $t('config.dd_wa_hdr_toggle_delay_desc_3') }}
</div>
<Checkbox id="dd_wa_hdr_toggle"
locale-prefix="config"
v-model="config.dd_wa_hdr_toggle"
default="false"
></Checkbox>
</div>
<!-- Config revert delay -->

View File

@@ -17,13 +17,6 @@ const config = ref(props.config)
<input type="number" min="1" max="3" class="form-control" id="min_fps_factor" placeholder="1" v-model="config.min_fps_factor" />
<div class="form-text">{{ $t('config.min_fps_factor_desc') }}</div>
</div>
<!--max_bitrate-->
<div class="mb-3">
<label for="max_bitrate" class="form-label">{{ $t("config.max_bitrate") }}</label>
<input type="number" class="form-control" id="max_bitrate" placeholder="0" v-model="config.max_bitrate" />
<div class="form-text">{{ $t("config.max_bitrate_desc") }}</div>
</div>
</template>
<style scoped>

View File

@@ -94,10 +94,10 @@
method: "POST",
body: JSON.stringify(this.passwordData),
}).then((r) => {
if (r.status === 200) {
if (r.status == 200) {
r.json().then((rj) => {
this.success = rj.status;
if (this.success === true) {
if (rj.status.toString() === "true") {
this.success = true;
setTimeout(() => {
document.location.reload();
}, 5000);

View File

@@ -42,7 +42,7 @@
fetch("./api/pin", {method: "POST", body: b})
.then((response) => response.json())
.then((response) => {
if (response.status === true) {
if (response.status.toString().toLowerCase() === "true") {
document.querySelector(
"#status"
).innerHTML = `<div class="alert alert-success" role="alert">${this.i18n.t('pin.pair_success')}</div>`;

View File

@@ -160,7 +160,7 @@
"dd_config_revert_delay_desc": "Additional delay in milliseconds to wait before reverting configuration when the app has been closed or the last session terminated. Main purpose is to provide a smoother transition when quickly switching between apps.",
"dd_config_revert_on_disconnect": "Config revert on disconnect",
"dd_config_revert_on_disconnect_desc": "Revert configuration upon disconnect of all clients instead of app close or last session termination.",
"dd_config_verify_only": "Verify that the display is enabled",
"dd_config_verify_only": "Verify that the display is enabled (default)",
"dd_hdr_option": "HDR",
"dd_hdr_option_auto": "Switch on/off the HDR mode as requested by the client (default)",
"dd_hdr_option_disabled": "Do not change HDR settings",
@@ -189,10 +189,8 @@
"dd_resolution_option_manual": "Use manually entered resolution",
"dd_resolution_option_manual_desc": "Enter the resolution to be used",
"dd_resolution_option_ogs_desc": "\"Optimize game settings\" option must be enabled on the Moonlight client for this to work.",
"dd_wa_hdr_toggle_delay_desc_1": "When using virtual display device (VDD) for streaming, it might incorrectly display HDR color. Sunshine can try to mitigate this issue, by turning HDR off and then on again.",
"dd_wa_hdr_toggle_delay_desc_2": "If the value is set to 0, the workaround is disabled (default). If the value is between 0 and 3000 milliseconds, Sunshine will turn off HDR, wait for the specified amount of time and then turn HDR on again. The recommended delay time is around 500 milliseconds in most cases.",
"dd_wa_hdr_toggle_delay_desc_3": "DO NOT use this workaround unless you actually have issues with HDR as it directly impacts stream start time!",
"dd_wa_hdr_toggle_delay": "High-contrast workaround for HDR",
"dd_wa_hdr_toggle_desc": "When using virtual display device as for streaming, it might display incorrect HDR color. With this option enabled, Sunshine will try to mitigate this issue.",
"dd_wa_hdr_toggle": "Enable high-contrast workaround for HDR",
"ds4_back_as_touchpad_click": "Map Back/Select to Touchpad Click",
"ds4_back_as_touchpad_click_desc": "When forcing DS4 emulation, map Back/Select to Touchpad Click",
"encoder": "Force a Specific Encoder",
@@ -254,8 +252,6 @@
"log_level_desc": "The minimum log level printed to standard out",
"log_path": "Logfile Path",
"log_path_desc": "The file where the current logs of Sunshine are stored.",
"max_bitrate": "Maximum Bitrate",
"max_bitrate_desc": "The maximum bitrate (in Kbps) that Sunshine will encode the stream at. If set to 0, it will always use the bitrate requested by Moonlight.",
"min_fps_factor": "Minimum FPS Factor",
"min_fps_factor_desc": "Sunshine will use this factor to calculate the minimum time between frames. Increasing this value slightly may help when streaming mostly static content. Higher values will consume more bandwidth.",
"min_threads": "Minimum CPU Thread Count",

View File

@@ -120,14 +120,13 @@
</div>
<ul id="client-list" class="list-group list-group-flush list-group-item-light" v-if="clients && clients.length > 0">
<div v-for="client in clients" class="list-group-item d-flex">
<div class="p-2 flex-grow-1">{{ client.name !== "" ? client.name : $t('troubleshooting.unpair_single_unknown') }}</div>
<div class="me-2 ms-auto btn btn-danger" @click="unpairSingle(client.uuid)"><i class="fas fa-trash"></i></div>
<div class="p-2 flex-grow-1">{{client.name != "" ? client.name : $t('troubleshooting.unpair_single_unknown')}}</div><div class="me-2 ms-auto btn btn-danger" @click="unpairSingle(client.uuid)"><i class="fas fa-trash"></i></div>
</div>
</ul>
<ul v-else class="list-group list-group-flush list-group-item-light">
<div class="list-group-item p-3 text-center"><em>{{ $t('troubleshooting.unpair_single_no_devices') }}</em></div>
</ul>
</div>
<!-- Logs -->
<div class="card p-2 my-4">
@@ -177,7 +176,7 @@
actualLogs() {
if (!this.logFilter) return this.logs;
let lines = this.logs.split("\n");
lines = lines.filter(x => x.indexOf(this.logFilter) !== -1);
lines = lines.filter(x => x.indexOf(this.logFilter) != -1);
return lines.join("\n");
}
},
@@ -211,7 +210,7 @@
.then((r) => r.json())
.then((r) => {
this.closeAppPressed = false;
this.closeAppStatus = r.status;
this.closeAppStatus = r.status.toString() === "true";
setTimeout(() => {
this.closeAppStatus = null;
}, 5000);
@@ -223,7 +222,7 @@
.then((r) => r.json())
.then((r) => {
this.unpairAllPressed = false;
this.unpairAllStatus = r.status;
this.unpairAllStatus = r.status.toString() === "true";
setTimeout(() => {
this.unpairAllStatus = null;
}, 5000);
@@ -241,9 +240,9 @@
.then((response) => response.json())
.then((response) => {
const clientList = document.querySelector("#client-list");
if (response.status === true && response.named_certs && response.named_certs.length) {
if (response.status === 'true' && response.named_certs && response.named_certs.length) {
this.clients = response.named_certs.sort((a, b) => {
return (a.name.toLowerCase() > b.name.toLowerCase() || a.name === "" ? 1 : -1)
return (a.name.toLowerCase() > b.name.toLowerCase() || a.name == "" ? 1 : -1)
});
} else {
this.clients = [];
@@ -271,7 +270,7 @@
.then((r) => r.json())
.then((r) => {
this.ddResetPressed = false;
this.ddResetStatus = r.status;
this.ddResetStatus = r.status.toString() === "true";
setTimeout(() => {
this.ddResetStatus = null;
}, 5000);

View File

@@ -81,10 +81,10 @@
body: JSON.stringify(this.passwordData),
}).then((r) => {
this.loading = false;
if (r.status === 200) {
if (r.status == 200) {
r.json().then((rj) => {
this.success = rj.status;
if (this.success === true) {
if (rj.status.toString() === "true") {
this.success = true;
setTimeout(() => {
document.location.reload();
}, 5000);

View File

@@ -1,11 +1,2 @@
# Allows Sunshine to acces /dev/uinput
KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", TAG+="uaccess"
# Allows Sunshine to access /dev/uhid
KERNEL=="uhid", TAG+="uaccess"
# Joypads
KERNEL=="hidraw*" ATTRS{name}=="Sunshine PS5 (virtual) pad" MODE="0660", TAG+="uaccess"
SUBSYSTEMS=="input", ATTRS{name}=="Sunshine X-Box One (virtual) pad", MODE="0660", TAG+="uaccess"
SUBSYSTEMS=="input", ATTRS{name}=="Sunshine gamepad (virtual) motion sensors", MODE="0660", TAG+="uaccess"
SUBSYSTEMS=="input", ATTRS{name}=="Sunshine Nintendo (virtual) pad", MODE="0660", TAG+="uaccess"

View File

@@ -1,111 +0,0 @@
@echo off
setlocal EnableDelayedExpansion
rem Check if parameter is provided
if "%~1"=="" (
echo Usage: %0 [add^|remove]
echo add - Adds Sunshine directories to system PATH
echo remove - Removes Sunshine directories from system PATH
exit /b 1
)
rem Get sunshine root directory
for %%I in ("%~dp0\..") do set "ROOT_DIR=%%~fI"
echo Sunshine root directory: !ROOT_DIR!
rem Define directories to add to path
set "PATHS_TO_MANAGE[0]=!ROOT_DIR!"
set "PATHS_TO_MANAGE[1]=!ROOT_DIR!\tools"
rem System path registry location
set "KEY_NAME=HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
set "VALUE_NAME=Path"
rem Get the current path
for /f "tokens=2*" %%A in ('reg query "%KEY_NAME%" /v "%VALUE_NAME%"') do set "CURRENT_PATH=%%B"
echo Current path: !CURRENT_PATH!
rem Check if adding to path
if /i "%~1"=="add" (
set "NEW_PATH=!CURRENT_PATH!"
rem Process each directory to add
for /L %%i in (0,1,1) do (
set "DIR_TO_ADD=!PATHS_TO_MANAGE[%%i]!"
rem Check if path already contains this directory
echo "!CURRENT_PATH!" | findstr /i /c:"!DIR_TO_ADD!" > nul
if !ERRORLEVEL!==0 (
echo !DIR_TO_ADD! already in path
) else (
echo Adding to path: !DIR_TO_ADD!
set "NEW_PATH=!NEW_PATH!;!DIR_TO_ADD!"
)
)
rem Only update if path was changed
if "!NEW_PATH!" neq "!CURRENT_PATH!" (
rem Set the new path in the registry
reg add "%KEY_NAME%" /v "%VALUE_NAME%" /t REG_EXPAND_SZ /d "!NEW_PATH!" /f
if !ERRORLEVEL!==0 (
echo Successfully added Sunshine directories to PATH
) else (
echo Failed to add Sunshine directories to PATH
)
) else (
echo No changes needed to PATH
)
exit /b !ERRORLEVEL!
)
rem Check if removing from path
if /i "%~1"=="remove" (
set "CHANGES_MADE=0"
rem Process each directory to remove
for /L %%i in (0,1,1) do (
set "DIR_TO_REMOVE=!PATHS_TO_MANAGE[%%i]!"
rem Check if path contains this directory
echo "!CURRENT_PATH!" | findstr /i /c:"!DIR_TO_REMOVE!" > nul
if !ERRORLEVEL!==0 (
echo Removing from path: !DIR_TO_REMOVE!
rem Build a new path by parsing and filtering the current path
set "NEW_PATH="
for %%p in ("!CURRENT_PATH:;=" "!") do (
set "PART=%%~p"
if /i "!PART!" NEQ "!DIR_TO_REMOVE!" (
if defined NEW_PATH (
set "NEW_PATH=!NEW_PATH!;!PART!"
) else (
set "NEW_PATH=!PART!"
)
)
)
set "CURRENT_PATH=!NEW_PATH!"
set "CHANGES_MADE=1"
) else (
echo !DIR_TO_REMOVE! not found in path
)
)
rem Only update if path was changed
if "!CHANGES_MADE!"=="1" (
rem Set the new path in the registry
reg add "%KEY_NAME%" /v "%VALUE_NAME%" /t REG_EXPAND_SZ /d "!CURRENT_PATH!" /f
if !ERRORLEVEL!==0 (
echo Successfully removed Sunshine directories from PATH
) else (
echo Failed to remove Sunshine directories from PATH
)
) else (
echo No changes needed to PATH
)
exit /b !ERRORLEVEL!
)
echo Unknown parameter: %~1
echo Usage: %0 [add^|remove]
exit /b 1