From 5793ff74ebd68f92ec824563b823531ce80c303e Mon Sep 17 00:00:00 2001 From: "Gurubase.io" Date: Thu, 2 Jan 2025 00:05:56 +0300 Subject: [PATCH 01/20] docs: Introducing Sunshine Guru on Gurubase.io (#3466) Signed-off-by: Gurubase.io Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 72e074b9..d225543a 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ [![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) [![GHCR](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)](https://github.com/LizardByte/Sunshine/pkgs/container/sunshine) [![Winget Version](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==)](https://github.com/microsoft/winget-pkgs/tree/master/manifests/l/LizardByte/Sunshine) +[![Gurubase](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)](https://gurubase.io/g/sunshine) [![GitHub Workflow Status (CI)](https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/CI.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/Sunshine/actions/workflows/CI.yml?query=branch%3Amaster) [![GitHub Workflow Status (localize)](https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/localize.yml.svg?branch=master&label=localize%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/Sunshine/actions/workflows/localize.yml?query=branch%3Amaster) From bb79557a25e92f5413d3ab7a840a918282303e48 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 21:23:20 -0500 Subject: [PATCH 02/20] build(deps): bump the lizardbyte-actions group across 1 directory with 2 updates (#3504) Bumps the lizardbyte-actions group with 2 updates in the / directory: [LizardByte/setup-release-action](https://github.com/lizardbyte/setup-release-action) and [LizardByte/create-release-action](https://github.com/lizardbyte/create-release-action). Updates `LizardByte/setup-release-action` from 2024.919.143601 to 2025.102.14715 - [Release notes](https://github.com/lizardbyte/setup-release-action/releases) - [Commits](https://github.com/lizardbyte/setup-release-action/compare/v2024.919.143601...v2025.102.14715) Updates `LizardByte/create-release-action` from 2024.919.143026 to 2025.102.13208 - [Release notes](https://github.com/lizardbyte/create-release-action/releases) - [Commits](https://github.com/lizardbyte/create-release-action/compare/v2024.919.143026...v2025.102.13208) --- updated-dependencies: - dependency-name: LizardByte/setup-release-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: lizardbyte-actions - dependency-name: LizardByte/create-release-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: lizardbyte-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/CI.yml | 12 ++++++------ .github/workflows/ci-docker.yml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9bf13e2a..5ebf9ed2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -41,7 +41,7 @@ jobs: - name: Setup Release id: setup_release - uses: LizardByte/setup-release-action@v2024.919.143601 + uses: LizardByte/setup-release-action@v2025.102.14715 with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -299,7 +299,7 @@ jobs: - name: Create/Update GitHub Release if: ${{ needs.setup_release.outputs.publish_release == 'true' }} - uses: LizardByte/create-release-action@v2024.919.143026 + uses: LizardByte/create-release-action@v2025.102.13208 with: allowUpdates: true body: ${{ needs.setup_release.outputs.release_body }} @@ -513,7 +513,7 @@ jobs: - name: Create/Update GitHub Release if: ${{ needs.setup_release.outputs.publish_release == 'true' }} - uses: LizardByte/create-release-action@v2024.919.143026 + uses: LizardByte/create-release-action@v2025.102.13208 with: allowUpdates: true body: ${{ needs.setup_release.outputs.release_body }} @@ -656,7 +656,7 @@ jobs: if: >- matrix.release && needs.setup_release.outputs.publish_release == 'true' - uses: LizardByte/create-release-action@v2024.919.143026 + uses: LizardByte/create-release-action@v2025.102.13208 with: allowUpdates: true artifacts: '${{ github.workspace }}/homebrew/*' @@ -946,7 +946,7 @@ jobs: - name: Create/Update GitHub Release if: ${{ needs.setup_release.outputs.publish_release == 'true' }} - uses: LizardByte/create-release-action@v2024.919.143026 + uses: LizardByte/create-release-action@v2025.102.13208 with: allowUpdates: true body: ${{ needs.setup_release.outputs.release_body }} @@ -1229,7 +1229,7 @@ jobs: - name: Create/Update GitHub Release if: ${{ needs.setup_release.outputs.publish_release == 'true' }} - uses: LizardByte/create-release-action@v2024.919.143026 + uses: LizardByte/create-release-action@v2025.102.13208 with: allowUpdates: true body: ${{ needs.setup_release.outputs.release_body }} diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index b561d9bf..128de505 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -115,7 +115,7 @@ jobs: - name: Setup Release id: setup_release - uses: LizardByte/setup-release-action@v2024.919.143601 + uses: LizardByte/setup-release-action@v2025.102.14715 with: dotnet: ${{ needs.check_dockerfiles.outputs.dotnet }} github_token: ${{ secrets.GITHUB_TOKEN }} @@ -344,7 +344,7 @@ jobs: - name: Create/Update GitHub Release if: ${{ needs.setup_release.outputs.publish_release == 'true' && steps.prepare.outputs.artifacts == 'true' }} - uses: LizardByte/create-release-action@v2024.919.143026 + uses: LizardByte/create-release-action@v2025.102.13208 with: allowUpdates: true artifacts: "*artifacts/*" From d50611c79bd8d49b88fa52456c1522b7845300f9 Mon Sep 17 00:00:00 2001 From: LizardByte-bot <108553330+LizardByte-bot@users.noreply.github.com> Date: Wed, 1 Jan 2025 22:23:11 -0500 Subject: [PATCH 03/20] chore(l10n): update translations (#3473) --- docs/configuration.md | 18 +- src/config.cpp | 4 + .../assets/web/configs/tabs/General.vue | 4 + .../assets/web/public/assets/locale/bg.json | 407 ++++++++++++++++++ .../assets/web/public/assets/locale/de.json | 6 +- .../web/public/assets/locale/en_GB.json | 4 +- .../web/public/assets/locale/en_US.json | 4 +- .../assets/web/public/assets/locale/pl.json | 407 ++++++++++++++++++ .../web/public/assets/locale/pt_BR.json | 407 ++++++++++++++++++ .../assets/web/public/assets/locale/sv.json | 50 +-- .../assets/web/public/assets/locale/uk.json | 407 ++++++++++++++++++ .../assets/web/public/assets/locale/zh.json | 2 +- 12 files changed, 1686 insertions(+), 34 deletions(-) create mode 100644 src_assets/common/assets/web/public/assets/locale/bg.json create mode 100644 src_assets/common/assets/web/public/assets/locale/pl.json create mode 100644 src_assets/common/assets/web/public/assets/locale/pt_BR.json create mode 100644 src_assets/common/assets/web/public/assets/locale/uk.json diff --git a/docs/configuration.md b/docs/configuration.md index 659e3d29..3b558071 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -57,7 +57,11 @@ editing the `conf` file in a text editor. Use the examples as reference. @endcode - Choices + Choices + bg + Bulgarian + + de German @@ -89,10 +93,18 @@ editing the `conf` file in a text editor. Use the examples as reference. ja Japanese + + pl + Polish + pt Portuguese + + pt_BR + Portuguese (Brazilian) + ru Russian @@ -105,6 +117,10 @@ editing the `conf` file in a text editor. Use the examples as reference. tr Turkish + + uk + Ukranian + zh Chinese (Simplified) diff --git a/src/config.cpp b/src/config.cpp index 81d084dc..32b43127 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1118,6 +1118,7 @@ namespace config { } string_restricted_f(vars, "locale", config::sunshine.locale, { + "bg"sv, // Bulgarian "de"sv, // German "en"sv, // English "en_GB"sv, // English (UK) @@ -1126,10 +1127,13 @@ namespace config { "fr"sv, // French "it"sv, // Italian "ja"sv, // Japanese + "pl"sv, // Polish "pt"sv, // Portuguese + "pt_BR"sv, // Portuguese (Brazilian) "ru"sv, // Russian "sv"sv, // Swedish "tr"sv, // Turkish + "uk"sv, // Ukrainian "zh"sv, // Chinese }); diff --git a/src_assets/common/assets/web/configs/tabs/General.vue b/src_assets/common/assets/web/configs/tabs/General.vue index 5349a3c4..9596e622 100644 --- a/src_assets/common/assets/web/configs/tabs/General.vue +++ b/src_assets/common/assets/web/configs/tabs/General.vue @@ -33,6 +33,7 @@ function removeCmd(index) {
{{ $t('config.locale_desc') }}
diff --git a/src_assets/common/assets/web/public/assets/locale/bg.json b/src_assets/common/assets/web/public/assets/locale/bg.json new file mode 100644 index 00000000..4ac627e6 --- /dev/null +++ b/src_assets/common/assets/web/public/assets/locale/bg.json @@ -0,0 +1,407 @@ +{ + "_common": { + "apply": "Прилагане", + "auto": "Автоматично", + "autodetect": "Автоматично откриване (препоръчително)", + "beta": "(бета)", + "cancel": "Отказ", + "disabled": "Изключено", + "disabled_def": "Изключено (по подразбиране)", + "dismiss": "Отхвърляне", + "do_cmd": "Команда преди стартиране", + "elevated": "Изпълнение като администратор", + "enabled": "Включено", + "enabled_def": "Включено (по подразбиране)", + "error": "Грешка!", + "note": "Забележка:", + "password": "Парола", + "run_as": "Стартиране като администратор", + "save": "Запазване", + "see_more": "Вижте повече", + "success": "Успешно!", + "undo_cmd": "Команда след приключване", + "username": "Потребителско име", + "warning": "Внимание!" + }, + "apps": { + "actions": "Действия", + "add_cmds": "Добавяне на команди", + "add_new": "Добавяне на ново", + "app_name": "Име на приложението", + "app_name_desc": "Име на приложението, както се показва в Moonlight", + "applications_desc": "Приложенията се опресняват при рестартиране на клиента", + "applications_title": "Приложения", + "auto_detach": "Продължаване на предаването, ако приложението се затвори бързо", + "auto_detach_desc": "По този начин ще се направи опит за автоматично разпознаване на приложения от тип „стартираща програма“, които се затварят бързо след като стартират друга програма или на друго свое копие. Когато се засече такова, то се третира като разкачено приложение.", + "cmd": "Команда", + "cmd_desc": "Основното приложение, което да се стартира. Ако е празно, няма да се стартира никакво приложение.", + "cmd_note": "Ако пътят до изпълнимия файл на командата съдържа интервали, трябва да го заградите с кавички.", + "cmd_prep_desc": "Списък с команди, които да се изпълняват преди/след това приложение. Ако някоя от подготвителните команди се провали, стартирането на приложението се прекъсва.", + "cmd_prep_name": "Подготвителни команди", + "covers_found": "Намерени обложки", + "delete": "Изтриване", + "detached_cmds": "Разкачени команди", + "detached_cmds_add": "Добавяне на разкачена команда", + "detached_cmds_desc": "Списък с команди, които да се изпълняват във фонов режим.", + "detached_cmds_note": "Ако пътят до изпълнимия файл на командата съдържа интервали, трябва да го заградите с кавички.", + "edit": "Редактиране", + "env_app_id": "Идентификатор на приложението", + "env_app_name": "Име на приложението", + "env_client_audio_config": "Конфигурацията на звука, поискана от клиента (2.0/5.1/7.1)", + "env_client_enable_sops": "Клиентът е заявил опцията за оптимизиране на играта за оптимално поточно предаване (true/false)", + "env_client_fps": "Заявените от клиента кадри/сек (целочислена стойност)", + "env_client_gcmap": "Заявената маска за контролера, във формат на битова маска (целочислена стойност)", + "env_client_hdr": "HDR е включен от клиента (true/false)", + "env_client_height": "Височината, заявена от клиента (целочислена стойност)", + "env_client_host_audio": "Клиентът е поискал звука да се изпълнява на отдалечения компютър (true/false)", + "env_client_width": "Ширината, заявена от клиента (целочислена стойност)", + "env_displayplacer_example": "Пример за автоматизиране на резолюцията чрез displayplacer:", + "env_qres_example": "Пример за автоматизиране на резолюцията чрез QRes:", + "env_qres_path": "Път до qres", + "env_var_name": "Име на променливата", + "env_vars_about": "Относно променливите на средата", + "env_vars_desc": "Всички команди получават тези променливи на средата по подразбиране:", + "env_xrandr_example": "Пример за автоматизиране на резолюцията чрез Xrandr:", + "exit_timeout": "Време за изчакване при затваряне", + "exit_timeout_desc": "Брой секунди за изчакване на всички процеси на приложението да завършат самостоятелно, когато бъде изпратена заявка за затваряне. Ако не е зададено, по подразбиране се изчаква до 5 секунди. Ако е зададено 0 или отрицателна стойност, приложението ще бъде прекратено незабавно.", + "find_cover": "Търсене на обложка", + "global_prep_desc": "Включване/изключване на изпълнението на глобалните подготвителни команди за това приложение.", + "global_prep_name": "Глобални команди за подготовка", + "image": "Изображение", + "image_desc": "Пътят до иконката/картинката/изображението на приложението, което ще бъде изпратено на клиента. Изображението трябва да е файл във формата PNG. Ако не е зададено, Sunshine ще изпрати стандартно изображение.", + "loading": "Зареждане…", + "name": "Име", + "output_desc": "Файлът, в който се запазва изходът (текстовия поток) от командата. Ако не е посочен, изходът се игнорира.", + "output_name": "Изход", + "run_as_desc": "Това може да е необходимо за някои приложения, които изискват администраторски права, за да работят правилно.", + "wait_all": "Поточното предаване да продължава, докато всички процеси на приложението не се затворят", + "wait_all_desc": "По този начин поточното предаване ще продължи, докато всички процеси, стартирани от приложението, не завършат изпълнението си. Ако няма отметка, предаването ще спре, когато първоначалният процес на приложението завърши, дори и да има други процеси от приложението, които все още работят.", + "working_dir": "Работна директория", + "working_dir_desc": "Работната директория, която да се подаде на процеса. Някои приложения, например, използват работната директория, за да търсят конфигурационни файлове. Ако не е зададена, по подразбиране ще се ползва директорията, в която се намира командата." + }, + "config": { + "adapter_name": "Име на устройството", + "adapter_name_desc_linux_1": "Ръчно задаване на графичния процесор, който да се използва за прихващане на екрана.", + "adapter_name_desc_linux_2": "за намиране на всички устройства, които могат да използват VAAPI", + "adapter_name_desc_linux_3": "Заменете ``renderD129`` с устройството върнато от по-горната команда, за да видите името и възможностите на устройството. За да бъде поддържано от Sunshine, то трябва задължително да има поне:", + "adapter_name_desc_windows": "Ръчно задаване на графичния процесор, който да се използва за прихващане на екрана. Ако не е зададено, графичният процесор се избира автоматично. Силно препоръчваме да оставите това поле празно, за да се направи автоматичен избор! Забележка: към този графичен процесор трябва да има свързан и включен екран. Съответните стойности могат да бъдат намерени чрез следната команда:", + "adapter_name_placeholder_windows": "Radeon RX 580 Series", + "add": "Добавяне", + "address_family": "Вид адреси", + "address_family_both": "IPv4+IPv6", + "address_family_desc": "Задайте вида адреси, използвани от Sunshine", + "address_family_ipv4": "Само IPv4", + "always_send_scancodes": "Винаги да се пращат скан-кодове", + "always_send_scancodes_desc": "Изпращането на скан-кодове подобрява съвместимостта с игри и приложения, но може да доведе до неправилно разчетени входни сигнали от клавиатурата при някои клиенти, ако не се ползва клавиатурна подредба съвместима с английски (САЩ). Включете това, ако въвеждането от клавиатурата изобщо не работи в някои приложения. Изключете го, ако клавишите на клиента пращат грешни входни сигнали към отдалечения компютър.", + "amd_coder": "Кодиране чрез AMF (H264)", + "amd_coder_desc": "Позволява избирането на ентропия при кодирането, така че да се даде приоритет на качеството или скростта на кодиране. Само за H.264.", + "amd_enforce_hrd": "Принудителна настройка на декодера с хипотетични справки (HRD) на AMF", + "amd_enforce_hrd_desc": "Увеличава ограниченията за контрол на скоростта, така че да се спазват изискванията на модела на HRD. Това значително намалява превишаването на идеалната побитова скорост, но може да предизвика дефекти при кодирането или влошаване на качеството при определени видео карти.", + "amd_preanalysis": "Предварителен анализ на AMF", + "amd_preanalysis_desc": "Дава възможност за предварителен анализ на контрола на скоростта, който може да повиши качеството за сметка на увеличено забавяне на кодирането.", + "amd_quality": "Качество на AMF", + "amd_quality_balanced": "балансирано – балансирано (по подразбиране)", + "amd_quality_desc": "По този начин се контролира балансът между скоростта и качеството на кодиране.", + "amd_quality_group": "Настройки за качеството на AMF", + "amd_quality_quality": "качество – предпочитане на качеството", + "amd_quality_speed": "скорост – предпочитане на скоростта", + "amd_rc": "Управление на скоростта на AMF", + "amd_rc_cbr": "cbr – постоянна побитова скорост (препоръчва се, ако HRD е включено)", + "amd_rc_cqp": "cqp – режим на постоянно qp", + "amd_rc_desc": "Това задава метода за управление на скоростта, така че да се гарантира, че няма да се превишава целевата побитова скорост на клиента. „cqp“ не е подходящ метод за подсигуряване на определената побитова скорост, а другите възможности (с изключение на „vbr_latency“) зависят от Принудителната настройка на HRD, която да помогне с ограничаването на превишаванията на побитовата скорост.", + "amd_rc_group": "Настройки за управление на скоростта на AMF", + "amd_rc_vbr_latency": "vbr_latency – променлива побитова скорост, ограничена от забавянето (препоръчва се, ако HRD е изключен; по подразбиране)", + "amd_rc_vbr_peak": "vbr_peak – променлива побитова скорост, ограничена от максимумите", + "amd_usage": "Използване на AMF", + "amd_usage_desc": "С тази настройка се задава основният профил на кодиране. Всички настройки по-долу нагласят подмножество от профила на използване, но има зададени и допълнителни скрити настройки, които не могат да бъдат променени другаде.", + "amd_usage_lowlatency": "lowlatency ниско забавяне (най-бързо)", + "amd_usage_lowlatency_high_quality": "lowlatency_high_quality – ниско забавяне, високо качество (бързо)", + "amd_usage_transcoding": "transcoding – транскодиране (най-бавно)", + "amd_usage_ultralowlatency": "ultralowlatency – ултра ниско забавяне (най-бързо; по подразбиране)", + "amd_usage_webcam": "webcam – уеб камера (бавно)", + "amd_vbaq": "Базирано на вариации адаптивно квантуване на AMF (VBAQ)", + "amd_vbaq_desc": "Човешкото зрение обикновено е по-малко чувствително към дефекти в места с много текстури. В режима VBAQ дисперсията на пикселите се използва за разпознаване на сложността на пространствените текстури, което позволява на кодирането да заделя повече битове за по-гладките области. Включването на тази функция води до подобряване на субективното визуално качество при някои видове съдържание.", + "apply_note": "Натиснете „Прилагане“, за да рестартирате Sunshine и да приложите промените. Това ще прекрати всички текущи сесии.", + "audio_sink": "Звуков изход", + "audio_sink_desc_linux": "Името на звуковия изход, използван за връщане на звука. Ако не е зададено, pulseaudio ще избере мониторното устройство по подразбиране. Можете да намерите името на звуковия изход като използвате някоя от тези команди:", + "audio_sink_desc_macos": "Името на звуковия изход, използван за връщане на звука. Sunshine може да получи достъп само до микрофоните в macOS, поради системни ограничения. За поточно предаване на системен звук ще Ви трябва помощта на Soundflower или BlackHole.", + "audio_sink_desc_windows": "Ръчно задаване на конкретно звуково устройство за прихващане. Ако не е зададено, устройството се избира автоматично. Силно препоръчително е да оставите това поле празно, за да използвате автоматичния избор на устройство! Ако имате няколко звукови устройства с еднакви имена, може да научите идентификаторите им чрез следната команда:", + "audio_sink_placeholder_macos": "BlackHole 2 канала", + "audio_sink_placeholder_windows": "Високоговорители (звуково устройство с високо качество)", + "av1_mode": "Поддръжка на AV1", + "av1_mode_0": "Sunshine ще обявява поддръжката на AV1 въз основа на възможностите за кодиране (препоръчително)", + "av1_mode_1": "Sunshine няма да обявява поддръжката на AV1", + "av1_mode_2": "Sunshine ще обявява поддръжката на AV1 Main 8-битов профил", + "av1_mode_3": "Sunshine ще обявява поддръжката на AV1 Main 8-битов и 10-битов (HDR) профили", + "av1_mode_desc": "Позволява на клиента да поиска видео поток с кодиране AV1 Main 8-битов или 10-битов. Кодирането на AV1 натоварва повече процесора, така че включването на тази настройка може да намали производителността при използване на софтуерно кодиране.", + "back_button_timeout": "Време на изчакване за симулиране на бутона Home/Guide", + "back_button_timeout_desc": "Ако бутонът Back/Select се задържи натиснат за определения брой милисекунди, се симулира натискане на бутона Home/Guide. Ако е зададена стойност < 0 (по подразбиране), задържането на бутона Back/Select няма да симулира натискането на бутона Home/Guide.", + "capture": "Принудително използване на определен метод на прихващане", + "capture_desc": "В автоматичния режим Sunshine ще използва първия, който работи. NvFBC изисква коригирани драйвери на nvidia.", + "cert": "Сертификат", + "cert_desc": "Сертификатът, който да се ползва за сдвояване на уеб интерфейса и клиента Moonlight. За по-добра съвместимост той трябва да има публичен ключ от вида RSA-2048.", + "channels": "Максимален брой свързани клиенти", + "channels_desc_1": "Sunshine може да позволи една сесия за поточно предаване да бъде споделена с множество клиенти едновременно.", + "channels_desc_2": "Някои методи за хардуерно кодиране могат да имат ограничения, които намаляват производителността при излъчване на повече от един поток.", + "coder_cabac": "cabac – контекстно-адаптивно двоично аритметично кодиране – по-високо качество", + "coder_cavlc": "cavlc – контекстно адаптивно кодиране с променлива дължина – по-бързо декодиране", + "configuration": "Настройки", + "controller": "Управление чрез контролер", + "controller_desc": "Позволява на клиентите да управляват отдалечения компютър с контролер", + "credentials_file": "Файл с удостоверителни данни", + "credentials_file_desc": "Съхраняване на потребителското име/парола отделно от файла за състоянието на Sunshine.", + "ds4_back_as_touchpad_click": "Симулиране на бутона Back/Select чрез натискане на сензорния панел", + "ds4_back_as_touchpad_click_desc": "При принудително симулиране на контролер DualShock 4, да се симулира натискането на бутона Back/Select чрез натискане на сензорния панел", + "encoder": "Принудително използване на определен метод на кодиране", + "encoder_desc": "Принудително използване на определен метод на кодиране. Ако не е зададено, Sunshine ще избере най-добрия наличен вариант. Забележка: ако използвате Уиндоус и изберете хардуерно кодиране, то трябва да се поддържа от видео картата, към която е свързан екранът.", + "encoder_software": "Софтуерно", + "external_ip": "Външен IP адрес", + "external_ip_desc": "Ако не е зададен външен IP адрес, Sunshine автоматично ще открие какъв е той", + "fec_percentage": "Процент на FEC", + "fec_percentage_desc": "Процент на пакетите за коригиране на грешки от всеки пакет данни за всеки видео кадър. По-високите стойности могат да коригират по-голяма загуба на мрежови пакети, но за сметка на увеличаване на данните предавани по мрежата.", + "ffmpeg_auto": "auto – нека ffmpeg реши (по подразбиране)", + "file_apps": "Файл с приложения", + "file_apps_desc": "Файлът, в който се съхраняват настройките на приложенията в Sunshine.", + "file_state": "Файл за състоянието", + "file_state_desc": "Файлът, в който се съхранява текущото състояние на Sunshine", + "gamepad": "Симулиран вид контролер", + "gamepad_auto": "Опции за автоматичен избор", + "gamepad_desc": "Изберете какъв вид контролер да бъде симулиран на отдалечения компютър", + "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds5": "DS5 (PS5)", + "gamepad_switch": "Nintendo Pro (Switch)", + "gamepad_manual": "Ръчни настройки за DS4", + "gamepad_x360": "X360 (Xbox 360)", + "gamepad_xone": "XOne (Xbox One)", + "global_prep_cmd": "Подготвителни команди", + "global_prep_cmd_desc": "Настройване на списък с команди, които да се изпълняват преди или след дадено приложение. Ако някоя от посочените подготвителни команди се провали, процесът на стартиране на приложението ще бъде прекъснат.", + "hevc_mode": "Поддръжка на HEVC", + "hevc_mode_0": "Sunshine ще обявява поддръжката на HEVC въз основа на възможностите за кодиране (препоръчително)", + "hevc_mode_1": "Sunshine няма да обявява поддръжката на HEVC", + "hevc_mode_2": "Sunshine ще обявява поддръжката на HEVC, профил Main", + "hevc_mode_3": "Sunshine ще обявява поддръжката на HEVC, профили Main и Main10 (HDR)", + "hevc_mode_desc": "Позволява на клиента да поиска видео поток с кодиране HEVC Main или HEVC Main10. Кодирането на HEVC натоварва повече процесора, така че включването на тази настройка може да намали производителността при използване на софтуерно кодиране.", + "high_resolution_scrolling": "Поддръжка на превъртане с висока резолюция", + "high_resolution_scrolling_desc": "Когато това е включено, Sunshine просто ще препредава командите за превъртане на колелцето на мишката с висока резолюция, идващи от клиенти използващи Moonlight. Може да е по-добре това да бъде изключено за по-старите приложения, в които превъртането може да мести съдържанието твърде бързо, ако събитията за превъртане са с висока резолюция.", + "install_steam_audio_drivers": "Инсталиране на аудио драйверите на Steam", + "install_steam_audio_drivers_desc": "Ако Steam е инсталиран, това автоматично ще инсталира и драйвера за поточно предаване на звук на Steam, чрез който може да се поддържа 5.1/7.1 обемен звук, както и да се заглушава звука на отдалечения компютър.", + "key_repeat_delay": "Забавяне на повторението на клавишите", + "key_repeat_delay_desc": "Колко бързо да започва повтарянето на симулираното натискане на клавишите, при задържането им в натиснато положение. Това е първоначалното закъснение в милисекунди преди да за почне повторението на клавишите.", + "key_repeat_frequency": "Честота на повтаряне на клавишите", + "key_repeat_frequency_desc": "Колко често да се извършват симулирани натискания на клавишите в секунда, при задържане на клавишите в натиснато положение. Стойността тук може да бъде и нецяло число.", + "key_rightalt_to_key_windows": "Изплозване на десния Alt като клавиша Windows", + "key_rightalt_to_key_win_desc": "Възможно е натискането на клавиша Windows да не може да бъде изпратено към сървъра от Moonlight. В тези случаи може да настроите Sunshine да мисли, че десният Alt е клавишът Windows.", + "keyboard": "Управление чрез клавиатура", + "keyboard_desc": "Позволява на клиентите да управляват отдалечения компютър с клавиатура", + "lan_encryption_mode": "Режим на шифроване в LAN", + "lan_encryption_mode_1": "Включено за поддържаните клиенти", + "lan_encryption_mode_2": "Задължително за всички клиенти", + "lan_encryption_mode_desc": "Това определя дали да се използва шифроване при излъчване в локалната мрежа. Шифроването може да намали производителността на излъчването, особено при не особено мощни сървъри и клиенти.", + "locale": "Език", + "locale_desc": "Език на потребителския интерфейс на Sunshine.", + "log_level": "Ниво на съобщенията в журнала", + "log_level_0": "Verbose", + "log_level_1": "Debug", + "log_level_2": "Info", + "log_level_3": "Warning", + "log_level_4": "Error", + "log_level_5": "Fatal", + "log_level_6": "Нищо", + "log_level_desc": "Минималното ниво на съобщения в журнала, извеждан на стандартния изход", + "log_path": "Път до журналния файл", + "log_path_desc": "Файлът, в който се запазва текущия журнал на Sunshine.", + "min_fps_factor": "Коефициент на минимален брой кадри/сек", + "min_fps_factor_desc": "Sunshine ще използва този коефициент за изчисляване на минималното време между кадрите. Лекото увеличаване на тази стойност може да помогне при излъчване на предимно статично съдържание. По-високите стойности ще създадат по-голям мрежов трафик.", + "min_threads": "Минимален брой нишки на процесора", + "min_threads_desc": "Увеличаването на стойността леко намалява ефективността на кодирането, но компромисът обикновено си заслужава, тъй като ще се използват повече процесорни ядра за кодиране. Идеалната стойност е най-ниската възможна стойност, при която кодирането може да се извършва надеждно при желаните от настройки за излъчване и използваният хардуер.", + "misc": "Други настройки", + "motion_as_ds4": "Симулиране на контролер DS4, ако контролерът на клиента разполага с поддръжка на сензори за движение", + "motion_as_ds4_desc": "Ако е изключено, сензорите за движение няма да се вземат предвид при избора на вида контролер.", + "mouse": "Управление чрез мишка", + "mouse_desc": "Позволява на клиентите да управляват отдалечения компютър с мишка", + "native_pen_touch": "Собствена поддръжка на писалка/докосване", + "native_pen_touch_desc": "Когато е включено, Sunshine просто ще препредава командите идващи от писалка/докосване както са получени от клиентите използващи Moonlight. Може да е по-добре това да бъде изключено за по-старите приложения, които няма собствена поддръжка на писалка/докосване.", + "notify_pre_releases": "Известия за предварителни версии", + "notify_pre_releases_desc": "Дали да бъдете уведомявани за нови предварителни версии на Sunshine, преди превръщането им в официални", + "nvenc_h264_cavlc": "Предпочитане на CAVLC пред CABAC за H.264", + "nvenc_h264_cavlc_desc": "Опростен вариант за ентропия при кодирането. CAVLC се нуждае от около 10% повече побитова скорост за същото качество. Има смисъл само за много стари декодиращи устройства.", + "nvenc_latency_over_power": "Предпочитане на по-малкото забавяне пред икономията на енергия", + "nvenc_latency_over_power_desc": "Sunshine изисква от графичния процесор да работи на максималната си тактова честота по време на излъчване, за да намали забавянето при кодирането. Изключването на тази настройка не се препоръчва, тъй като това може да доведе до значително увеличаване на закъснението при кодиране.", + "nvenc_opengl_vulkan_on_dxgi": "Изчертаване на OpenGL/Vulkan върху DXGI", + "nvenc_opengl_vulkan_on_dxgi_desc": "Sunshine не може да прихваща с пълна честота на кадрите програми, реализирани с OpenGL или Vulkan, работещи на цял екран, освен ако не се изчертават върху DXGI. Това е генерална системна настройка, която ще бъде върната в първоначалното си състояние при затваряне на процеса на Sunshine.", + "nvenc_preset": "Настройка за производителност", + "nvenc_preset_1": "(най-бързо, по подразбиране)", + "nvenc_preset_7": "(най-бавно)", + "nvenc_preset_desc": "По-големите числа подобряват компресията (качеството при дадена побитова скорост) за сметка на увеличено забавяне при кодирането. Препоръчително е това да се променя само ако има ограничение от мрежата или декодера. В противен случай подобен ефект може да се постигне чрез увеличаване на побитовата скорост.", + "nvenc_realtime_hags": "Използване на реално-времеви приоритет при хардуерно-ускореното планиране на задачите на графичния процесор (HAGS)", + "nvenc_realtime_hags_desc": "В момента драйверите на NVIDIA могат да засичат при кодиране, ако HAGS е включено, използва се реално-времеви приоритет и използването на видео паметта е близо до максимума. Изключването на тази опция понижава приоритета до „висок“, заобикаляйки засичането за сметка на намалена производителност на прихващане на екрана, когато графичният процесор е силно натоварен.", + "nvenc_spatial_aq": "Пространствено AQ", + "nvenc_spatial_aq_desc": "Използване на по-високи стойности на QP за по-простите области във видеото. Препоръчително е това да бъде включено, когато се излъчва с по-ниска побитова скорост.", + "nvenc_spatial_aq_disabled": "Изключено (по-бързо, по подразбиране)", + "nvenc_spatial_aq_enabled": "Включено (по-бавно)", + "nvenc_twopass": "Режим на две преминавания", + "nvenc_twopass_desc": "Добавя предварителна стъпка на кодиране. Това позволява да се открият повече вектори на движение, да се разпредели по-добре побитовата скорост в рамките на кадъра, както и да се спазват по-стриктно ограниченията на побитовата скорост. Изключването не се препоръчва, тъй като това може да доведе до периодично превишаване на зададената побитова скорост и последваща загуба на пакети.", + "nvenc_twopass_disabled": "Изключено (най-бързо, не се препоръчва)", + "nvenc_twopass_full_res": "Пълна резолюция (по-бавно)", + "nvenc_twopass_quarter_res": "Четвърт резолюция (по-бързо, по подразбиране)", + "nvenc_vbv_increase": "Процентно увеличение на VBV/HRD в един кадър", + "nvenc_vbv_increase_desc": "По подразбиране Sunshine използва VBV/HRD в един кадър, което означава, че размерът на всеки кодиран видео кадър не се очаква да превишава желаната побитова скорост, разделена на желаната честота на кадрите. Отслабването на това ограничение може да бъде от полза и да действа като променлива побитова скорост с ниско забавяне, но същевременно може да доведе до загуба на пакети, ако мрежата не разполага с достатъчен буфер, за да се справи с пиковете на побитова скорост. Максимално допустимата стойност е 400, което съответства на 5 пъти увеличен максимален размер на кодирания видео кадър.", + "origin_web_ui_allowed": "Разрешение за достъп до уеб интерфейса", + "origin_web_ui_allowed_desc": "Определя от къде може да се ползва уеб интерфейсът. Това не отменя нуждата от въвеждане на потребителско име и парола.", + "origin_web_ui_allowed_lan": "Само устройства в локалната мрежа имат достъп до уеб интерфейса", + "origin_web_ui_allowed_pc": "Само компютърът, на който работи Sunshine, има достъп до уеб интерфейса", + "origin_web_ui_allowed_wan": "Всеки има достъп до уеб интерфейса", + "output_name_desc_unix": "По време на стартирането на Sunshine би трябвало да видите списък с откритите екрани. Забележка: трябва да използвате стойността на идентификатора в скобите. По-долу е даден пример – действителният екран може да бъде намерен в раздела за Отстраняване на проблеми.", + "output_name_desc_windows": "Ръчно задаване на идентификатор на екран, който да се ползва за прихващане на картината. Ако не е зададено, ще се използва основният екран. Забележка: ако сте посочили конкретна видео карта по-горе, този екран трябва да е свързан към същата. По време на стартирането на Sunshine би трябвало да видите списък с откритите екрани. По-долу е даден пример – действителният екран може да бъде намерен в раздела за Отстраняване на проблеми.", + "output_name_unix": "Номер на екрана", + "output_name_windows": "Идентификатора на екрана", + "ping_timeout": "Време за изчакване на отговор", + "ping_timeout_desc": "Продължителност от време в милисекунди, през което да се изчаква за получаване на данни от Moonlight, преди да се прекрати потокът", + "pkey": "Частен ключ", + "pkey_desc": "Частният ключ, използван за уеб интерфейса и при сдвояване с клиента на Moonlight. За най-добра съвместимост е добре това да бъде частен ключ от вида RSA-2048.", + "port": "Порт", + "port_alert_1": "Sunshine не може да използва портове с номера по-малки 1024!", + "port_alert_2": "Не могат да се ползват портове с номера по-големи от 65535!", + "port_desc": "Задаване на групата от портове, които да се използват от Sunshine", + "port_http_port_note": "Използвайте този порт, за да се свържете с Moonlight.", + "port_note": "Бележка", + "port_port": "Порт", + "port_protocol": "Протокол", + "port_tcp": "TCP", + "port_udp": "UDP", + "port_warning": "Даването на достъп до уеб интерфейса от Интернет представлява риск за сигурността! Действате на своя отговорност!", + "port_web_ui": "Уеб интерфейс", + "qp": "Параметър на квантуване (QP)", + "qp_desc": "Някои устройства може да не поддържат постоянна побитова скорост на предаване. За тях вместо това се използва параметъра на кватуване. По-високите стойности означават по-голяма компресия, но и по-ниско качество.", + "qsv_coder": "Кодиране чрез QuickSync (H264)", + "qsv_preset": "Настройка на QuickSync", + "qsv_preset_fast": "бързо (ниско качество)", + "qsv_preset_faster": "по-бързо (по-ниско качество)", + "qsv_preset_medium": "средно (по подразбиране)", + "qsv_preset_slow": "бавно (добро качество)", + "qsv_preset_slower": "по-бавно (по-добро качество)", + "qsv_preset_slowest": "най-бавно (най-добро качество)", + "qsv_preset_veryfast": "най-бързо (най-ниско качество)", + "qsv_slow_hevc": "Разрешаване на бавното кодиране чрез HEVC", + "qsv_slow_hevc_desc": "Това може да даде възможност за кодиране чрез HEVC при ползване на по-стари графични процесори на Intel, за сметка на по-голямо използване на графичния процесор и по-ниска производителност.", + "restart_note": "Sunshine се рестартира, за да се приложат промените.", + "sunshine_name": "Име на Sunshine", + "sunshine_name_desc": "Името, показвано в Moonlight за този сървър. Ако не е посочено, се използва името на компютъра.", + "sw_preset": "Настройка на софтуерното кодиране", + "sw_preset_desc": "Оптимизиране на баланса между скоростта на кодиране (брой кодирани кадри в секунда) и ефективността на компресиране (качество за бит в битовия поток). Стандартната стойност е „супер бързо“.", + "sw_preset_fast": "бързо", + "sw_preset_faster": "по-бързо", + "sw_preset_medium": "средно", + "sw_preset_slow": "бавно", + "sw_preset_slower": "по-бавно", + "sw_preset_superfast": "супер бързо (по подразбиране)", + "sw_preset_ultrafast": "ултра бързо", + "sw_preset_veryfast": "много бързо", + "sw_preset_veryslow": "много бавно", + "sw_tune": "Фина настройка на софтуерното кодиране", + "sw_tune_animation": "animation – подходящо за анимационни филми; използва по-висока степен на деблокиране и повече референтни кадри", + "sw_tune_desc": "Опции за фина настройка, които се прилагат след зададената по-горе настройка. Стандартната стойност е „zerolatency“.", + "sw_tune_fastdecode": "fastdecode – позволява по-бързо декодиране чрез изключване на определени филтри", + "sw_tune_film": "film – подходящо за висококачествено филмово съдържание; намалява деблокирането", + "sw_tune_grain": "grain – запазва зърнестата структура типична за по-стари филмови материали", + "sw_tune_stillimage": "stillimage – подходящо за съдържание с малко движение, подобно на презентация", + "sw_tune_zerolatency": "zerolatency – подходящо за бързо кодиране и предаване с ниско забавяне (по подразбиране)", + "touchpad_as_ds4": "Симулиране на контролер DS4, ако контролерът на клиента разполага със сензорен панел", + "touchpad_as_ds4_desc": "Ако е изключено, сензорният панел няма да се взема предвид при избора на вида контролер.", + "upnp": "UPnP", + "upnp_desc": "Автоматично настройване на пренасочването на портове за излъчване през Интернет", + "vaapi_strict_rc_buffer": "Налагане на строги ограничения за побитовата скорост на кадрите за H.264/HEVC при видео карти на AMD", + "vaapi_strict_rc_buffer_desc": "Включването на това може да предотврати пропускането на кадри по мрежата по време на промени в сцената, но качеството на видеото по време на движение може да бъде намалено.", + "virtual_sink": "Виртуален изход", + "virtual_sink_desc": "Ръчно задаване на виртуално звуково устройство, което да се ползва. Ако не е зададено, устройството се избира автоматично. Силно препоръчително е да оставите това поле празно, за да използвате автоматичния избор на устройство!", + "virtual_sink_placeholder": "Виртуални високоговорители на Steam", + "vt_coder": "Кодиране на VideoToolbox", + "vt_realtime": "Кодиране в реално време на VideoToolbox", + "vt_software": "Софтуерно кодиране на VideoToolbox", + "vt_software_allowed": "Разрешено", + "vt_software_forced": "Принудително", + "wan_encryption_mode": "Режим на шифроване в WAN", + "wan_encryption_mode_1": "Включено за поддържаните клиенти (по подразбиране)", + "wan_encryption_mode_2": "Задължително за всички клиенти", + "wan_encryption_mode_desc": "Това определя дали да се използва шифроване при излъчване през Интернет. Шифроването може да намали производителността на излъчването, особено при не особено мощни сървъри и клиенти." + }, + "index": { + "description": "Sunshine е сървър за собствено поточно предаване на игри, предназначен за ползване с Moonlight.", + "download": "Сваляне", + "installed_version_not_stable": "Използвате предварителна версия на Sunshine. Възможно е да се сблъскате с различни видове проблеми. Моля, съобщавайте за всички проблеми, които срещате. Благодарим, че помагате да направим Sunshine по-добър софтуер!", + "loading_latest": "Зареждане на последната версия…", + "new_pre_release": "Има нова версия предварителна версия!", + "new_stable": "Има нова стабилна версия!", + "startup_errors": "Внимание! Sunshine засече тези грешки по време на стартиране. НАИСТИНА Е ПРЕПОРЪЧИТЕЛНО да ги отстраните, преди да започнете излъчването.", + "version_dirty": "Благодарим, че помогнахте да направим Sunshine по-добър софтуер!", + "version_latest": "Използвате най-новата версия на Sunshine", + "welcome": "Здравейте от Sunshine!" + }, + "navbar": { + "applications": "Приложения", + "configuration": "Настройки", + "home": "Начало", + "password": "Промяна на паролата", + "pin": "ПИН", + "theme_auto": "Автоматично", + "theme_dark": "Тъмна", + "theme_light": "Светла", + "toggle_theme": "Цветова схема", + "troubleshoot": "Отстраняване на проблеми" + }, + "password": { + "confirm_password": "Потвърждаване на паролата", + "current_creds": "Текущи данни за идентификация", + "new_creds": "Нови данни за идентификация", + "new_username_desc": "Ако не бъде посочено, потребителското име няма да бъде променено", + "password_change": "Промяна на паролата", + "success_msg": "Паролата е променена успешно! Тази страница скоро ще се презареди и браузърът ще поиска да въведете новите данни за идентификация." + }, + "pin": { + "device_name": "Име на устройството", + "pair_failure": "Неуспешно сдвояване. Проверете дали ПИН кодът е въведен правилно.", + "pair_success": "Успешно сдвояване! Проверете Moonlight, за да продължите.", + "pin_pairing": "Сдвояване чрез ПИН код", + "send": "Изпращане", + "warning_msg": "Уверете се, че имате достъп до клиента, с който сдвоявате сървъра. Този софтуер може да предостави пълен контрол върху компютъра, затова внимавайте!" + }, + "resource_card": { + "github_discussions": "Дискусии в GitHub", + "legal": "Правни въпроси", + "legal_desc": "Използвайки този софтуер, Вие се съгласявате с правилата и условията в следните документи.", + "license": "Лиценз", + "lizardbyte_website": "Уеб сайт на LizardByte", + "resources": "Ресурси", + "resources_desc": "Ресурси за Sunshine!", + "third_party_notice": "Забележка относно ползването на имената на трети страни" + }, + "troubleshooting": { + "force_close": "Принудително затваряне", + "force_close_desc": "Ако Moonlight се оплаква, че дадено приложение работи в момента, принудителното затваряне на това приложение би трябвало да реши проблема.", + "force_close_error": "Грешка при затваряне на приложението", + "force_close_success": "Приложението е затворено успешно!", + "logs": "Журнал", + "logs_desc": "Разгледайте съобщенията в журнала на Sunshine", + "logs_find": "Търсене…", + "restart_sunshine": "Рестартиране на Sunshine", + "restart_sunshine_desc": "Ако Sunshine не работи правилно, можете да опитате да го рестартирате. Това ще прекрати всички текущи сесии.", + "restart_sunshine_success": "Sunshine се рестартира", + "troubleshooting": "Отстраняване на проблеми", + "unpair_all": "Премахване на сдвояването с всички клиенти", + "unpair_all_error": "Грешка при премахването на сдвояванията", + "unpair_all_success": "Премахнато е сдвояването с всички устройства.", + "unpair_desc": "Премахване на всички сдвоени устройства. Устройствата, чието сдвояване бъде премахнато, докато имат активна сесия, ще останат свързани, но няма да могат да започнат нови сесии или да възобновят вече започнати такива.", + "unpair_single_no_devices": "Няма сдвоени устройства.", + "unpair_single_success": "Въпреки това едно или повече устройства може все още да са в активна сесия. Използвайте бутона „Принудително затваряне“ по-горе, за да прекратите всички активни сесии.", + "unpair_single_unknown": "Неизвестен клиент", + "unpair_title": "Премахване на сдвояването с устройствата" + }, + "welcome": { + "confirm_password": "Потвърждаване на паролата", + "create_creds": "Преди да започнете, трябва да си създадете ново потребителско име и парола за достъп до уеб интерфейса.", + "create_creds_alert": "Данните по-долу ще са необходими за достъп до уеб интерфейса на Sunshine. Пазете ги, тъй като няма да ги видите отново!", + "greeting": "Добре дошли в Sunshine!", + "login": "Вписване", + "welcome_success": "Тази страница скоро ще се презареди и браузърът ще поиска да въведете новите данни за идентификация" + } +} diff --git a/src_assets/common/assets/web/public/assets/locale/de.json b/src_assets/common/assets/web/public/assets/locale/de.json index 641090c1..b298ebcc 100644 --- a/src_assets/common/assets/web/public/assets/locale/de.json +++ b/src_assets/common/assets/web/public/assets/locale/de.json @@ -28,7 +28,7 @@ "add_cmds": "Befehle hinzufügen", "add_new": "Neu hinzufügen", "app_name": "Anwendungsname", - "app_name_desc": "Anwendungsname, wie auf Mondlicht gezeigt", + "app_name_desc": "Name der App, wie in Moonlight angezeigt", "applications_desc": "Anwendungen werden nur beim Neustart des Clients aktualisiert", "applications_title": "Anwendungen", "auto_detach": "Streaming fortsetzen, wenn die Anwendung schnell beendet wird", @@ -256,7 +256,7 @@ "output_name_unix": "Anzeigenummer", "output_name_windows": "Ausgabename", "ping_timeout": "Ping-Timeout", - "ping_timeout_desc": "Wie lange warten Sie in Millisekunden auf Daten von Mondlicht bevor Sie den Strom herunterfahren", + "ping_timeout_desc": "Verzögerung in Millisekunden beim Warten auf Daten von Moonlight bevor der Stream beendet wird", "pkey": "Privater Schlüssel", "pkey_desc": "Der private Schlüssel, der für das Web-UI- und Moonlight-Client-Paar verwendet wird. Für bestmögliche Kompatibilität sollte dies ein privater RSA-2048 Schlüssel sein.", "port": "Port", @@ -360,7 +360,7 @@ "pin": { "device_name": "Gerätename", "pair_failure": "Paarung fehlgeschlagen: Prüfen Sie, ob die PIN korrekt eingegeben wurde", - "pair_success": "Erfolgreich! Bitte überprüfe Mondlicht um fortzufahren", + "pair_success": "Erfolg! Weiter geht es in Moonlight", "pin_pairing": "PIN Pairing", "send": "Senden", "warning_msg": "Stellen Sie sicher, dass Sie Zugriff auf den Client haben, mit dem Sie sich verbinden. Diese Software kann Ihrem Computer die totale Kontrolle geben, also seien Sie vorsichtig!" diff --git a/src_assets/common/assets/web/public/assets/locale/en_GB.json b/src_assets/common/assets/web/public/assets/locale/en_GB.json index ca455f3e..ee7a849b 100644 --- a/src_assets/common/assets/web/public/assets/locale/en_GB.json +++ b/src_assets/common/assets/web/public/assets/locale/en_GB.json @@ -252,9 +252,9 @@ "origin_web_ui_allowed_pc": "Only localhost may access Web UI", "origin_web_ui_allowed_wan": "Anyone may access Web UI", "output_name_desc_unix": "During Sunshine startup, you should see the list of detected displays. Note: You need to use the id value inside the parenthesis.", - "output_name_desc_windows": "Manually specify a display to use for capture. If unset, the primary display is captured. Note: If you specified a GPU above, this display must be connected to that GPU. The appropriate values can be found using the following command:", + "output_name_desc_windows": "Manually specify a display device id to use for capture. If unset, the primary display is captured. Note: If you specified a GPU above, this display must be connected to that GPU. During Sunshine startup, you should see the list of detected displays. Below is an example; the actual output can be found in the Troubleshooting tab.", "output_name_unix": "Display number", - "output_name_windows": "Output Name", + "output_name_windows": "Display Device Id", "ping_timeout": "Ping Timeout", "ping_timeout_desc": "How long to wait in milliseconds for data from moonlight before shutting down the stream", "pkey": "Private Key", diff --git a/src_assets/common/assets/web/public/assets/locale/en_US.json b/src_assets/common/assets/web/public/assets/locale/en_US.json index a41617fe..b277c8af 100644 --- a/src_assets/common/assets/web/public/assets/locale/en_US.json +++ b/src_assets/common/assets/web/public/assets/locale/en_US.json @@ -252,9 +252,9 @@ "origin_web_ui_allowed_pc": "Only localhost may access Web UI", "origin_web_ui_allowed_wan": "Anyone may access Web UI", "output_name_desc_unix": "During Sunshine startup, you should see the list of detected displays. Note: You need to use the id value inside the parenthesis. Below is an example; the actual output can be found in the Troubleshooting tab.", - "output_name_desc_windows": "Manually specify a display to use for capture. If unset, the primary display is captured. Note: If you specified a GPU above, this display must be connected to that GPU. The appropriate values can be found using the following command:", + "output_name_desc_windows": "Manually specify a display device id to use for capture. If unset, the primary display is captured. Note: If you specified a GPU above, this display must be connected to that GPU. During Sunshine startup, you should see the list of detected displays. Below is an example; the actual output can be found in the Troubleshooting tab.", "output_name_unix": "Display number", - "output_name_windows": "Output Name", + "output_name_windows": "Display Device Id", "ping_timeout": "Ping Timeout", "ping_timeout_desc": "How long to wait in milliseconds for data from moonlight before shutting down the stream", "pkey": "Private Key", diff --git a/src_assets/common/assets/web/public/assets/locale/pl.json b/src_assets/common/assets/web/public/assets/locale/pl.json new file mode 100644 index 00000000..c0ed0b35 --- /dev/null +++ b/src_assets/common/assets/web/public/assets/locale/pl.json @@ -0,0 +1,407 @@ +{ + "_common": { + "apply": "Zastosuj", + "auto": "Automatyczny", + "autodetect": "Autowykrywanie (zalecane)", + "beta": "(beta)", + "cancel": "Anuluj", + "disabled": "Wyłączony", + "disabled_def": "Wyłączone (domyślnie)", + "dismiss": "Odrzuć", + "do_cmd": "Polecenie Do", + "elevated": "Podwyższone", + "enabled": "Włączone", + "enabled_def": "Włączone (domyślnie)", + "error": "Błąd!", + "note": "Uwaga:", + "password": "Hasło", + "run_as": "Uruchom jako administrator", + "save": "Zapisz", + "see_more": "Zobacz więcej", + "success": "Sukces!", + "undo_cmd": "Polecenie Undo", + "username": "Nazwa użytkownika", + "warning": "Ostrzeżenie!" + }, + "apps": { + "actions": "Działania", + "add_cmds": "Dodaj polecenia", + "add_new": "Dodaj nowy", + "app_name": "Nazwa aplikacji", + "app_name_desc": "Nazwa aplikacji wyświetlana w aplikacji Moonlight", + "applications_desc": "Aplikacje są odświeżane tylko po ponownym uruchomieniu klienta", + "applications_title": "Aplikacje", + "auto_detach": "Kontynuuj przesyłanie strumieniowe, jeśli aplikacja szybko się wyłączy", + "auto_detach_desc": "Spowoduje to próbę automatycznego wykrycia aplikacji typu launcher, które zamykają się szybko po uruchomieniu innego programu lub własnej instancji. Po wykryciu aplikacji typu launcher jest ona traktowana jako aplikacja odłączona.", + "cmd": "Polecenie", + "cmd_desc": "Główna aplikacja do uruchomienia. Jeśli puste, żadna aplikacja nie zostanie uruchomiona.", + "cmd_note": "Jeśli ścieżka do pliku wykonywalnego zawiera spacje, należy ująć ją w cudzysłów.", + "cmd_prep_desc": "Lista poleceń do uruchomienia przed/po tej aplikacji. Jeśli którekolwiek z poleceń wstępnych nie powiedzie się, uruchomienie aplikacji zostanie przerwane.", + "cmd_prep_name": "Polecenia przygotowujące", + "covers_found": "Znalezione okładki", + "delete": "Usuń", + "detached_cmds": "Polecenia odłączone", + "detached_cmds_add": "Dodaj odłączone polecenie", + "detached_cmds_desc": "Lista poleceń uruchamianych w tle.", + "detached_cmds_note": "Jeśli ścieżka do pliku wykonywalnego zawiera spacje, należy ująć ją w cudzysłów.", + "edit": "Edytuj", + "env_app_id": "Identyfikator aplikacji", + "env_app_name": "Nazwa aplikacji", + "env_client_audio_config": "Konfiguracja audio wymagana przez klienta (2.0/5.1/7.1)", + "env_client_enable_sops": "Klient zażądał opcji optymalizacji gry pod kątem optymalnego strumienia (prawda/fałsz)", + "env_client_fps": "FPS żądane przez klienta (liczba całkowita)", + "env_client_gcmap": "Żądana maska kontrolera w formacie zestawu bitów/pola bitów (liczba całkowita)", + "env_client_hdr": "HDR jest włączony przez klienta (prawda/fałsz)", + "env_client_height": "Wysokość żądana przez klienta (liczba całkowita)", + "env_client_host_audio": "Klient zażądał dźwięku hosta (prawda/fałsz)", + "env_client_width": "Szerokość żądana przez klienta (liczba całkowita)", + "env_displayplacer_example": "Przykład - displayplacer dla automatycznej rozdzielczości:", + "env_qres_example": "Przykład - QRes dla automatycznej rozdzielczości:", + "env_qres_path": "ścieżka qres", + "env_var_name": "Nazwa Var", + "env_vars_about": "Informacje o zmiennych środowiskowych", + "env_vars_desc": "Wszystkie polecenia domyślnie pobierają te zmienne środowiskowe:", + "env_xrandr_example": "Przykład - Xrandr dla automatycznej rozdzielczości:", + "exit_timeout": "Limit czasu wyjścia", + "exit_timeout_desc": "Liczba sekund oczekiwania, aż wszystkie procesy aplikacji zakończą działanie po żądaniu zakończenia. Jeśli nie jest ustawiona, domyślnie odczekiwane jest do 5 sekund. Jeśli ustawiona na zero lub wartość ujemną, aplikacja zostanie natychmiast zakończona.", + "find_cover": "Znajdź okładkę", + "global_prep_desc": "Włączenie/wyłączenie wykonywania globalnych poleceń przygotowawczych dla tej aplikacji.", + "global_prep_name": "Globalne polecenia przygotowawcze", + "image": "Obraz", + "image_desc": "Ścieżka ikony/obrazu/ścieżki aplikacji, która zostanie wysłany do klienta. Obraz musi być plikiem PNG. Jeśli nie zostanie ustawiony, Sunshine wyśle domyślny obraz pudełka.", + "loading": "Ładowanie...", + "name": "Nazwa", + "output_desc": "Plik, w którym przechowywane są dane wyjściowe polecenia, jeśli nie zostanie określony, dane wyjściowe zostaną zignorowane", + "output_name": "Wyjście", + "run_as_desc": "Może to być konieczne w przypadku niektórych aplikacji, które wymagają uprawnień administratora do prawidłowego działania.", + "wait_all": "Kontynuuj przesyłanie strumieniowe, aż wszystkie procesy aplikacji zakończą działanie", + "wait_all_desc": "Spowoduje to kontynuowanie przesyłania strumieniowego do momentu zakończenia wszystkich procesów uruchomionych przez aplikację. Jeśli opcja nie jest zaznaczona, strumień zostanie zatrzymany po zakończeniu początkowego procesu aplikacji, nawet jeśli inne procesy aplikacji są nadal uruchomione.", + "working_dir": "Katalog roboczy", + "working_dir_desc": "Katalog roboczy, który powinien zostać przekazany do procesu. Na przykład, niektóre aplikacje używają katalogu roboczego do wyszukiwania plików konfiguracyjnych. Jeśli nie zostanie ustawiony, Sunshine domyślnie wybierze katalog nadrzędny polecenia" + }, + "config": { + "adapter_name": "Nazwa adaptera", + "adapter_name_desc_linux_1": "Ręczne określenie procesora graficznego używanego do przechwytywania.", + "adapter_name_desc_linux_2": "aby znaleźć wszystkie urządzenia obsługujące VAAPI", + "adapter_name_desc_linux_3": "Zastąp ``renderD129`` urządzeniem z powyższej listy, aby wyświetlić nazwę i możliwości urządzenia. Aby być obsługiwanym przez Sunshine, musi mieć co najmniej:", + "adapter_name_desc_windows": "Ręczne określenie procesora graficznego używanego do przechwytywania. Jeśli nie zostanie ustawione, procesor graficzny zostanie wybrany automatycznie. Zdecydowanie zalecamy pozostawienie tego pola pustego, aby korzystać z automatycznego wyboru GPU! Uwaga: Ten procesor graficzny musi mieć podłączony i włączony wyświetlacz. Odpowiednie wartości można znaleźć za pomocą następującego polecenia:", + "adapter_name_placeholder_windows": "Seria Radeon RX 580", + "add": "Dodaj", + "address_family": "Rodzina adresów", + "address_family_both": "IPv4+IPv6", + "address_family_desc": "Ustaw rodzinę adresów używaną przez Sunshine", + "address_family_ipv4": "Tylko IPv4", + "always_send_scancodes": "Zawsze wysyłaj kody skanowania", + "always_send_scancodes_desc": "Wysyłanie kody skanów zwiększa kompatybilność z grami i aplikacjami, ale może powodować nieprawidłowe wprowadzanie danych z klawiatury przez niektórych klientów, którzy nie używają układu klawiatury Angielski (Stany Zjednoczone). Włącz, jeśli wprowadzanie danych z klawiatury w ogóle nie działa w niektórych aplikacjach. Wyłącz, jeśli klawisze na kliencie generują nieprawidłowe dane wejściowe na hoście.", + "amd_coder": "AMF Coder (H264)", + "amd_coder_desc": "Umożliwia wybór kodowania entropijnego w celu nadania priorytetu jakości lub szybkości kodowania. Tylko H.264.", + "amd_enforce_hrd": "Wymuszanie AMF Hypothetical Reference Decoder (HRD)", + "amd_enforce_hrd_desc": "Zwiększa ograniczenia kontroli szybkości, aby spełnić wymagania modelu HRD. Zmniejsza to znacznie przepełnienie bitrate, ale może powodować artefakty kodowania lub obniżenie jakości na niektórych kartach.", + "amd_preanalysis": "Wstępna analiza AMF", + "amd_preanalysis_desc": "Umożliwia to wstępną analizę kontroli szybkości, która może zwiększyć jakość kosztem zwiększonego opóźnienia kodowania.", + "amd_quality": "Jakość AMF", + "amd_quality_balanced": "balanced -- zrównoważony (domyślnie)", + "amd_quality_desc": "Kontroluje to równowagę między szybkością kodowania a jakością.", + "amd_quality_group": "Ustawienia jakości AMF", + "amd_quality_quality": "quality - preferuj jakość", + "amd_quality_speed": "speed - preferuj szybkość", + "amd_rc": "Kontrola prędkości AMF", + "amd_rc_cbr": "cbr -- stały bitrate (zalecane, jeśli włączona jest funkcja HRD)", + "amd_rc_cqp": "cqp -- stały tryb qp", + "amd_rc_desc": "Kontroluje to metodę kontroli szybkości, aby upewnić się, że nie przekraczamy docelowej przepływności klienta. \"cqp\" nie nadaje się do kierowania bitrate, a inne opcje poza \"vbr_latency\" zależą od wymuszenia HRD, aby pomóc ograniczyć przepełnienia bitrate.", + "amd_rc_group": "Ustawienia kontroli prędkości AMF", + "amd_rc_vbr_latency": "vbr_latency -- zmienna szybkość bitrate z ograniczonym opóźnieniem (zalecane, jeśli HRD jest wyłączone; domyślne)", + "amd_rc_vbr_peak": "vbr_peak -- zmienna przepływność ograniczona wartością szczytową", + "amd_usage": "Użycie AMF", + "amd_usage_desc": "Ustawia to podstawowy profil kodowania. Wszystkie opcje przedstawione poniżej zastąpią podzbiór profilu użytkowania, ale zastosowane zostaną dodatkowe ukryte ustawienia, których nie można skonfigurować w innym miejscu.", + "amd_usage_lowlatency": "lowlatency - niskie opóźnienie (najszybsze)", + "amd_usage_lowlatency_high_quality": "lowlatency_high_quality - niskie opóźnienie, wysoka jakość (szybko)", + "amd_usage_transcoding": "transcoding -- transkodowanie (najwolniejsze)", + "amd_usage_ultralowlatency": "ultralowlatency - ultra niskie opóźnienie (najszybsze; domyślne)", + "amd_usage_webcam": "webcam -- kamera internetowa (wolno)", + "amd_vbaq": "AMF Adaptacyjna kwantyzacja oparta na wariancji (VBAQ)", + "amd_vbaq_desc": "Ludzki system wizualny jest zazwyczaj mniej wrażliwy na artefakty w obszarach o wysokiej teksturze. W trybie VBAQ wariancja pikseli jest używana do wskazania złożoności tekstur przestrzennych, umożliwiając koderowi przydzielenie większej liczby bitów do gładszych obszarów. Włączenie tej funkcji prowadzi do poprawy subiektywnej jakości wizualnej w przypadku niektórych treści.", + "apply_note": "Kliknij \"Zastosuj\", aby ponownie uruchomić Sunshine i zastosować zmiany. Spowoduje to zakończenie wszystkich uruchomionych sesji.", + "audio_sink": "Wejście audio", + "audio_sink_desc_linux": "Nazwa odbiornika audio używanego dla Audio Loopback. Jeśli nie określisz tej zmiennej, pulseaudio wybierze domyślne urządzenie monitorujące. Nazwę urządzenia audio można znaleźć za pomocą polecenia:", + "audio_sink_desc_macos": "Nazwa odbiornika audio używanego dla Audio Loopback. Sunshine może uzyskać dostęp do mikrofonów tylko w systemie macOS ze względu na ograniczenia systemowe. Aby przesyłać strumieniowo dźwięk systemowy za pomocą Soundflower lub BlackHole.", + "audio_sink_desc_windows": "Ręczne określenie konkretnego urządzenia audio do przechwytywania. Jeśli nie jest ustawione, urządzenie zostanie wybrane automatycznie. Zdecydowanie zalecamy pozostawienie tego pola pustego, aby korzystać z automatycznego wyboru urządzenia! Jeśli masz wiele urządzeń audio o identycznych nazwach, możesz uzyskać identyfikator urządzenia za pomocą następującego polecenia:", + "audio_sink_placeholder_macos": "BlackHole 2ch", + "audio_sink_placeholder_windows": "Głośniki (High Definition Audio Device)", + "av1_mode": "Wsparcie AV1", + "av1_mode_0": "Sunshine będzie reklamować obsługę AV1 w oparciu o możliwości enkodera (zalecane)", + "av1_mode_1": "Sunshine nie będzie reklamować wsparcia dla AV1", + "av1_mode_2": "Sunshine będzie zalecać wsparcie dla 8-bitowego profilu AV1 Main", + "av1_mode_3": "Sunshine będzie zalecać obsługę profili AV1 Main 8-bit i 10-bit (HDR)", + "av1_mode_desc": "Umożliwia klientowi żądanie 8-bitowych lub 10-bitowych strumieni wideo AV1 Main. Kodowanie AV1 jest bardziej obciążające dla procesora, więc włączenie tej opcji może zmniejszyć wydajność podczas korzystania z kodowania programowego.", + "back_button_timeout": "Limit czasu emulacji przycisku Home/Guide", + "back_button_timeout_desc": "Jeśli przycisk Wstecz/Wybierz zostanie przytrzymany przez określoną liczbę milisekund, nastąpi emulacja naciśnięcia przycisku Home/Guide. Jeśli ustawiono wartość < 0 (domyślnie), przytrzymanie przycisku Wstecz/Wybierz nie będzie emulować przycisku Home/Guide.", + "capture": "Wymuś określoną metodę przechwytywania", + "capture_desc": "W trybie automatycznym Sunshine użyje pierwszego działającego sterownika. NvFBC wymaga poprawionych sterowników NVIDIA.", + "cert": "Certyfikat", + "cert_desc": "Certyfikat używany do parowania interfejsu użytkownika i klienta Moonlight. Aby uzyskać najlepszą kompatybilność, powinien on mieć klucz publiczny RSA-2048.", + "channels": "Maksymalna liczba połączonych klientów", + "channels_desc_1": "Sunshine pozwala na udostępnianie pojedynczej sesji streamingowej wielu klientom jednocześnie.", + "channels_desc_2": "Niektóre kodery sprzętowe mogą mieć ograniczenia, które zmniejszają wydajność przy wielu strumieniach.", + "coder_cabac": "cabac -- adaptacyjne binarne kodowanie arytmetyczne - wyższa jakość", + "coder_cavlc": "cavlc - adaptacyjne kodowanie kontekstowe o zmiennej długości - szybsze dekodowanie", + "configuration": "Konfiguracja", + "controller": "Włącz wejście kontrolera", + "controller_desc": "Umożliwia gościom kontrolowanie systemu hosta za pomocą gamepada / kontrolera", + "credentials_file": "Plik poświadczeń", + "credentials_file_desc": "Przechowuj nazwę użytkownika/hasło oddzielnie od pliku stanu Sunshine.", + "ds4_back_as_touchpad_click": "Mapuj przycisk Wstecz/Wybierz na kliknięcie panelu dotykowego", + "ds4_back_as_touchpad_click_desc": "Podczas wymuszania emulacji DS4, mapuj Back/Select na kliknięcie panelu dotykowego", + "encoder": "Wymuś określony koder", + "encoder_desc": "Wymuś określony koder, w przeciwnym razie Sunshine wybierze najlepszą dostępną opcję. Uwaga: Jeśli określisz koder sprzętowy w systemie Windows, musi on być zgodny z procesorem graficznym, do którego podłączony jest wyświetlacz.", + "encoder_software": "Oprogramowanie", + "external_ip": "Zewnętrzny adres IP", + "external_ip_desc": "Jeśli nie podano zewnętrznego adresu IP, Sunshine automatycznie wykryje zewnętrzny adres IP", + "fec_percentage": "Procent FEC", + "fec_percentage_desc": "Procent pakietów korekcji błędów na pakiet danych w każdej klatce wideo. Wyższe wartości mogą skorygować większą utratę pakietów sieciowych, ale kosztem zwiększenia wykorzystania przepustowości.", + "ffmpeg_auto": "auto -- niech ffmpeg zdecyduje (domyślnie)", + "file_apps": "Plik aplikacji", + "file_apps_desc": "Plik, w którym przechowywane są bieżące aplikacje Sunshine.", + "file_state": "Plik stanu", + "file_state_desc": "Plik, w którym przechowywany jest aktualny stan Sunshine", + "gamepad": "Emulowany typ kontrolera", + "gamepad_auto": "Opcje automatycznego wyboru", + "gamepad_desc": "Wybierz typ kontrolera, który ma być emulowany na hoście", + "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds5": "DS5 (PS5)", + "gamepad_switch": "Nintendo Pro (Switch)", + "gamepad_manual": "Ustawienia DS4", + "gamepad_x360": "X360 (Xbox 360)", + "gamepad_xone": "XOne (Xbox One)", + "global_prep_cmd": "Polecenia przygotowujące", + "global_prep_cmd_desc": "Konfiguruje listę poleceń do wykonania przed lub po uruchomieniu dowolnej aplikacji. Jeśli którekolwiek z określonych poleceń przygotowawczych nie powiedzie się, proces uruchamiania aplikacji zostanie przerwany.", + "hevc_mode": "Obsługa HEVC", + "hevc_mode_0": "Sunshine będzie zalecać obsługę HEVC w oparciu o możliwości kodera (zalecane)", + "hevc_mode_1": "Sunshine nie będzie zalecać wsparcia dla HEVC", + "hevc_mode_2": "Sunshine będzie zalecać wsparcie dla głównego profilu HEVC", + "hevc_mode_3": "Sunshine będzie zalecać obsługę profili HEVC Main i Main10 (HDR)", + "hevc_mode_desc": "Umożliwia klientowi żądanie strumieni wideo HEVC Main lub HEVC Main10. Kodowanie HEVC jest bardziej obciążające dla procesora, więc włączenie tej opcji może zmniejszyć wydajność podczas korzystania z kodowania programowego.", + "high_resolution_scrolling": "Obsługa przewijania w wysokiej rozdzielczości", + "high_resolution_scrolling_desc": "Po włączeniu Sunshine będzie przepuszczać zdarzenia przewijania w wysokiej rozdzielczości od klientów Moonlight. Może to być przydatne do wyłączenia w starszych aplikacjach, które przewijają zbyt szybko zdarzenia przewijania w wysokiej rozdzielczości.", + "install_steam_audio_drivers": "Zainstaluj sterowniki audio Steam", + "install_steam_audio_drivers_desc": "Jeśli zainstalowany jest Steam, automatycznie zainstalowany zostanie sterownik Steam Streaming Speakers do obsługi dźwięku przestrzennego 5.1/7.1 i wyciszania dźwięku hosta.", + "key_repeat_delay": "Opóźnienie powtarzania klawiszy", + "key_repeat_delay_desc": "Kontroluje szybkość powtarzania klawiszy. Początkowe opóźnienie w milisekundach przed powtarzaniem klawiszy.", + "key_repeat_frequency": "Częstotliwość powtarzania klawiszy", + "key_repeat_frequency_desc": "Jak często klawisze powtarzają się co sekundę. Ta konfigurowalna opcja obsługuje wartości dziesiętne.", + "key_rightalt_to_key_windows": "Mapuj klawisz prawy Alt na klawisz Windows", + "key_rightalt_to_key_win_desc": "Może się zdarzyć, że nie można wysłać klawisza Windows bezpośrednio z Moonlight. W takich przypadkach przydatne może być sprawienie, by Sunshine myślał, że prawy Alt jest klawiszem Windows", + "keyboard": "Włącz wejście klawiatury", + "keyboard_desc": "Umożliwia gościom kontrolowanie systemu hosta za pomocą klawiatury", + "lan_encryption_mode": "Tryb szyfrowania LAN", + "lan_encryption_mode_1": "Włączone dla obsługiwanych klientów", + "lan_encryption_mode_2": "Wymagane dla wszystkich klientów", + "lan_encryption_mode_desc": "Określa, kiedy szyfrowanie będzie używane podczas przesyłania strumieniowego przez sieć lokalną. Szyfrowanie może zmniejszyć wydajność przesyłania strumieniowego, szczególnie na mniej wydajnych hostach i klientach.", + "locale": "Język", + "locale_desc": "Ustawienia językowe używane w interfejsie użytkownika Sunshine.", + "log_level": "Poziom raportowania", + "log_level_0": "Rozszerzony", + "log_level_1": "Debugowanie", + "log_level_2": "Informacyjne", + "log_level_3": "Ostrzeżenia", + "log_level_4": "Błąd", + "log_level_5": "Krytyczny", + "log_level_6": "Brak", + "log_level_desc": "Minimalny poziom logów wyświetlany w konsoli", + "log_path": "Ścieżka pliku dziennika", + "log_path_desc": "Plik, w którym przechowywane są bieżące dzienniki Sunshine.", + "min_fps_factor": "Minimalny współczynnik FPS", + "min_fps_factor_desc": "Sunshine użyje tego współczynnika do obliczenia minimalnego czasu między klatkami. Nieznaczne zwiększenie tej wartości może pomóc w przypadku strumieniowania głównie treści statycznych. Wyższe wartości będą zużywać więcej przepustowości.", + "min_threads": "Minimalna liczba wątków procesora", + "min_threads_desc": "Zwiększenie wartości nieznacznie zmniejsza wydajność kodowania, ale kompromis jest zwykle warty tego, aby uzyskać wykorzystanie większej liczby rdzeni procesora do kodowania. Idealną wartością jest najniższa wartość, która pozwala na niezawodne kodowanie przy pożądanych ustawieniach strumieniowania na posiadanym sprzęcie.", + "misc": "Różne opcje", + "motion_as_ds4": "Emulacja kontrolera DS4, jeśli kliencki kontroler zgłasza obecność czujników ruchu", + "motion_as_ds4_desc": "Jeśli opcja ta jest wyłączona, czujniki ruchu nie będą brane pod uwagę podczas wyboru typu kontrolera.", + "mouse": "Włącz wejście myszy", + "mouse_desc": "Umożliwia gościom kontrolowanie systemu hosta za pomocą myszy", + "native_pen_touch": "Natywna obsługa pióra/dotyku", + "native_pen_touch_desc": "Po włączeniu Sunshine będzie przekazywać natywne zdarzenia pióra/dotyku z klienta Moonlight. Może to być przydatne do wyłączenia w starszych aplikacjach bez natywnej obsługi pióra/dotyku.", + "notify_pre_releases": "Powiadomienia o wydaniu wstępnym", + "notify_pre_releases_desc": "Czy otrzymywać powiadomienia o nowych przedpremierowych wersjach Sunshine", + "nvenc_h264_cavlc": "Preferowanie CAVLC nad CABAC w H.264", + "nvenc_h264_cavlc_desc": "Prostsza forma kodowania entropijnego. CAVLC wymaga około 10% więcej bitrate dla tej samej jakości. Dotyczy tylko naprawdę starych urządzeń dekodujących.", + "nvenc_latency_over_power": "Niższe opóźnienie kodowania przedkładane nad oszczędność energii", + "nvenc_latency_over_power_desc": "Sunshine żąda maksymalnej prędkości zegara GPU podczas strumieniowania, aby zmniejszyć opóźnienie kodowania. Wyłączenie tej funkcji nie jest zalecane, ponieważ może to prowadzić do znacznego zwiększenia opóźnień kodowania.", + "nvenc_opengl_vulkan_on_dxgi": "Prezentacja OpenGL/Vulkan na DXGI", + "nvenc_opengl_vulkan_on_dxgi_desc": "Sunshine nie może przechwytywać pełnoekranowych programów OpenGL i Vulkan z pełną liczbą klatek na sekundę, chyba że są one wyświetlane na DXGI. Jest to ustawienie ogólnosystemowe, które jest przywracane po wyjściu z programu Sunshine.", + "nvenc_preset": "Wstępne ustawienia wydajności", + "nvenc_preset_1": "(najszybszy, domyślny)", + "nvenc_preset_7": "(najwolniejszy)", + "nvenc_preset_desc": "Wyższe liczby poprawiają kompresję (jakość przy danym bitrate) kosztem zwiększonego opóźnienia kodowania. Zaleca się zmianę tylko wtedy, gdy jest to ograniczone przez sieć lub dekoder, w przeciwnym razie podobny efekt można osiągnąć poprzez zwiększenie bitrate.", + "nvenc_realtime_hags": "Użycie priorytetu czasu rzeczywistego w harmonogramie sprzętowej akceleracji procesora graficznego", + "nvenc_realtime_hags_desc": "Obecnie sterowniki NVIDIA mogą zawieszać się w koderze, gdy włączony jest HAGS, używany jest priorytet czasu rzeczywistego, a wykorzystanie pamięci VRAM jest bliskie maksimum. Wyłączenie tej opcji obniża priorytet do wysokiego, omijając zamrożenie kosztem zmniejszonej wydajności przechwytywania, gdy GPU jest mocno obciążony.", + "nvenc_spatial_aq": "Spatial AQ", + "nvenc_spatial_aq_desc": "Przypisuje wyższe wartości QP do płaskich regionów wideo. Zalecane włączenie podczas streamowania przy niższych przepływnościach.", + "nvenc_spatial_aq_disabled": "Wyłączone (szybciej, domyślnie)", + "nvenc_spatial_aq_enabled": "Włączone (wolniej)", + "nvenc_twopass": "Tryb dwuprzebiegowy", + "nvenc_twopass_desc": "Dodaje wstępne przejście kodowania. Pozwala to wykryć więcej wektorów ruchu, lepiej rozłożyć przepływność w ramce i ściślej przestrzegać limitów przepływności. Wyłączenie tej funkcji nie jest zalecane, ponieważ może to prowadzić do sporadycznych przekroczeń przepływności i późniejszej utraty pakietów.", + "nvenc_twopass_disabled": "Wyłączony (najszybszy, niezalecany)", + "nvenc_twopass_full_res": "Pełna rozdzielczość (wolniej)", + "nvenc_twopass_quarter_res": "Rozdzielczość ćwiartki (szybsza, domyślna)", + "nvenc_vbv_increase": "Procentowy wzrost VBV/HRD w pojedynczej ramce", + "nvenc_vbv_increase_desc": "Domyślnie Sunshine używa jednoklatkowego VBV/HRD, co oznacza, że żaden zakodowany rozmiar klatki wideo nie powinien przekraczać żądanej przepływności podzielonej przez żądaną liczbę klatek na sekundę. Złagodzenie tego ograniczenia może być korzystne i działać jako zmienna przepływność o niskim opóźnieniu, ale może również prowadzić do utraty pakietów, jeśli sieć nie ma bufora, aby obsłużyć skoki przepływności. Maksymalna akceptowana wartość to 400, co odpowiada 5-krotnemu zwiększeniu górnego limitu rozmiaru zakodowanej ramki wideo.", + "origin_web_ui_allowed": "Interfejs Origin Web UI dozwolony", + "origin_web_ui_allowed_desc": "Pochodzenie adresu zdalnego punktu końcowego, któremu nie odmówiono dostępu do interfejsu Web UI", + "origin_web_ui_allowed_lan": "Tylko osoby w sieci LAN mogą uzyskać dostęp do interfejsu użytkownika", + "origin_web_ui_allowed_pc": "Tylko localhost może uzyskać dostęp do Web UI", + "origin_web_ui_allowed_wan": "Każdy może uzyskać dostęp do Web UI", + "output_name_desc_unix": "Podczas uruchamiania Sunshine powinieneś zobaczyć listę wykrytych wyświetlaczy. Uwaga: Należy użyć wartości id wewnątrz nawiasu. Poniżej znajduje się przykład; rzeczywiste dane wyjściowe można znaleźć w zakładce Rozwiązywanie problemów.", + "output_name_desc_windows": "Ręczne określenie identyfikatora urządzenia wyświetlającego, które ma być używane do przechwytywania. Jeśli nie zostanie ustawione, przechwytywany będzie główny wyświetlacz. Uwaga: Jeśli powyżej określono procesor graficzny, ten wyświetlacz musi być do niego podłączony. Podczas uruchamiania Sunshine powinna zostać wyświetlona lista wykrytych wyświetlaczy. Poniżej znajduje się przykład; rzeczywisty wynik można znaleźć w zakładce Rozwiązywanie problemów.", + "output_name_unix": "Wyświetlany numer", + "output_name_windows": "Wyświetl identyfikator urządzenia", + "ping_timeout": "Limit czasu ping", + "ping_timeout_desc": "Jak długo czekać w milisekundach na dane z Moonlight przed zamknięciem strumienia", + "pkey": "Klucz prywatny", + "pkey_desc": "Klucz prywatny używany do parowania interfejsu użytkownika i klienta Moonlight. Aby zapewnić najlepszą kompatybilność, powinien to być klucz prywatny RSA-2048.", + "port": "Port", + "port_alert_1": "Sunshine nie może używać portów poniżej 1024!", + "port_alert_2": "Porty powyżej 65535 nie są dostępne!", + "port_desc": "Ustaw rodzinę portów używanych przez Sunshine", + "port_http_port_note": "Ten port służy do łączenia się z Moonlight.", + "port_note": "Uwaga", + "port_port": "Port", + "port_protocol": "Protokół", + "port_tcp": "TCP", + "port_udp": "UDP", + "port_warning": "Wystawienie interfejsu użytkownika na Internet stanowi zagrożenie dla bezpieczeństwa! Postępuj na własne ryzyko!", + "port_web_ui": "Web UI", + "qp": "Parametr kwantyzacji", + "qp_desc": "Niektóre urządzenia mogą nie obsługiwać stałej szybkości transmisji. W przypadku tych urządzeń zamiast tego używana jest wartość QP. Wyższa wartość oznacza większą kompresję, ale niższą jakość.", + "qsv_coder": "Koder QuickSync (H264)", + "qsv_preset": "Ustawienie wstępne QuickSync", + "qsv_preset_fast": "szybki (niska jakość)", + "qsv_preset_faster": "szybciej (niższa jakość)", + "qsv_preset_medium": "średni (domyślnie)", + "qsv_preset_slow": "wolny (dobra jakość)", + "qsv_preset_slower": "wolniej (lepsza jakość)", + "qsv_preset_slowest": "najwolniejszy (najlepsza jakość)", + "qsv_preset_veryfast": "najszybszy (najniższa jakość)", + "qsv_slow_hevc": "Zezwalaj na wolne kodowanie HEVC", + "qsv_slow_hevc_desc": "Może to umożliwić kodowanie HEVC na starszych procesorach graficznych Intel, kosztem wyższego wykorzystania GPU i gorszej wydajności.", + "restart_note": "Sunshine uruchamia się ponownie, aby zastosować zmiany.", + "sunshine_name": "Nazwa Sunshine", + "sunshine_name_desc": "Nazwa wyświetlana przez Moonlight. Jeśli nie zostanie określona, używana jest nazwa hosta komputera", + "sw_preset": "Ustawienia wstępne SW", + "sw_preset_desc": "Optymalizacja kompromisu między szybkością kodowania (zakodowane klatki na sekundę) a wydajnością kompresji (jakość na bit w strumieniu bitów). Domyślnie superszybki.", + "sw_preset_fast": "szybki", + "sw_preset_faster": "szybciej", + "sw_preset_medium": "średni", + "sw_preset_slow": "wolny", + "sw_preset_slower": "wolniejszy", + "sw_preset_superfast": "superszybki (domyślnie)", + "sw_preset_ultrafast": "ultraszybki", + "sw_preset_veryfast": "bardzo szybki", + "sw_preset_veryslow": "bardzo wolny", + "sw_tune": "Dostrajanie SW", + "sw_tune_animation": "animacja - dobra do kreskówek; wykorzystuje wyższe odblokowanie i więcej klatek referencyjnych", + "sw_tune_desc": "Opcje strojenia, które są stosowane po ustawieniu wstępnym. Domyślne ustawienie to zerolatency.", + "sw_tune_fastdecode": "fastdecode -- umożliwia szybsze dekodowanie poprzez wyłączenie niektórych filtrów", + "sw_tune_film": "Film - użycie dla wysokiej jakości treści filmowych; obniża deblocking", + "sw_tune_grain": "grain - zachowuje strukturę ziarna w starym, ziarnistym materiale filmowym", + "sw_tune_stillimage": "stillimage - dobre dla zawartości podobnej do pokazu slajdów", + "sw_tune_zerolatency": "zerolatency -- dobre dla szybkiego kodowania i strumieniowania z niskim opóźnieniem (domyślnie)", + "touchpad_as_ds4": "Emulacja kontrolera DS4, jeśli kliencki kontroler zgłasza obecność touchpada", + "touchpad_as_ds4_desc": "Jeśli opcja ta jest wyłączona, obecność touchpada nie będzie brana pod uwagę podczas wyboru typu kontrolera.", + "upnp": "UPnP", + "upnp_desc": "Automatycznie skonfiguruj przekierowanie portów do przesyłania strumieniowego przez Internet", + "vaapi_strict_rc_buffer": "Ścisłe egzekwowanie limitów przepływności klatek dla H.264/HEVC na układach GPU AMD", + "vaapi_strict_rc_buffer_desc": "Włączenie tej opcji pozwala uniknąć porzucania klatek przez sieć podczas zmiany sceny, ale jakość wideo może zostać obniżona podczas ruchu.", + "virtual_sink": "Wirtualny odbiornik", + "virtual_sink_desc": "Ręczne określenie używanego wirtualnego urządzenia audio. Jeśli nie jest ustawione, urządzenie zostanie wybrane automatycznie. Zdecydowanie zalecamy pozostawienie tego pola pustego, aby korzystać z automatycznego wyboru urządzenia!", + "virtual_sink_placeholder": "Głośniki do strumieniowania Steam", + "vt_coder": "Koder VideoToolbox", + "vt_realtime": "Kodowanie w czasie rzeczywistym VideoToolbox", + "vt_software": "Kodowanie oprogramowania VideoToolbox", + "vt_software_allowed": "Dozwolone", + "vt_software_forced": "Wymuszone", + "wan_encryption_mode": "Tryb szyfrowania WAN", + "wan_encryption_mode_1": "Włączone dla obsługiwanych klientów (domyślnie)", + "wan_encryption_mode_2": "Wymagane dla wszystkich klientów", + "wan_encryption_mode_desc": "Określa, kiedy szyfrowanie będzie używane podczas przesyłania strumieniowego przez Internet. Szyfrowanie może zmniejszyć wydajność przesyłania strumieniowego, szczególnie na mniej wydajnych hostach i klientach." + }, + "index": { + "description": "Sunshine jest samodzielnym hostem strumienia gry dla Moonlight.", + "download": "Pobierz", + "installed_version_not_stable": "Korzystasz z przedpremierowej wersji Sunshine. Mogą wystąpić błędy lub inne problemy. Prosimy o zgłaszanie wszelkich napotkanych problemów. Dziękujemy za pomoc w ulepszaniu oprogramowania Sunshine!", + "loading_latest": "Ładowanie najnowszej wersji...", + "new_pre_release": "Dostępna jest nowa wersja przedpremierowa!", + "new_stable": "Nowa stabilna wersja jest już dostępna!", + "startup_errors": "Uwaga! Sunshine wykrył te błędy podczas uruchamiania. ZDECYDOWANIE ZALECAMY ich naprawienie przed rozpoczęciem streamowania.", + "version_dirty": "Dziękujemy za pomoc w ulepszaniu oprogramowania Sunshine!", + "version_latest": "Korzystasz z najnowszej wersji Sunshine", + "welcome": "Witaj, Sunshine!" + }, + "navbar": { + "applications": "Aplikacje", + "configuration": "Konfiguracja", + "home": "Strona główna", + "password": "Zmień hasło", + "pin": "Pin", + "theme_auto": "Auto", + "theme_dark": "Ciemny", + "theme_light": "Jasny", + "toggle_theme": "Wygląd", + "troubleshoot": "Rozwiązywanie problemów" + }, + "password": { + "confirm_password": "Potwierdź hasło", + "current_creds": "Aktualne dane logowania", + "new_creds": "Nowe dane logowania", + "new_username_desc": "Jeśli nie zostanie podane, nazwa użytkownika nie ulegnie zmianie", + "password_change": "Zmiana hasła", + "success_msg": "Hasło zostało pomyślnie zmienione! Strona zostanie wkrótce przeładowana, a przeglądarka poprosi o podanie nowych danych uwierzytelniających." + }, + "pin": { + "device_name": "Nazwa urządzenia", + "pair_failure": "Parowanie nie powiodło się: Sprawdź, czy kod PIN został wpisany poprawnie", + "pair_success": "Sukces! Sprawdź Moonlight, aby kontynuować", + "pin_pairing": "Parowanie PIN", + "send": "Wyślij", + "warning_msg": "Upewnij się, że masz dostęp do klienta, z którym się łączysz. To oprogramowanie może dać całkowitą kontrolę nad komputerem, więc bądź ostrożny!" + }, + "resource_card": { + "github_discussions": "Dyskusje GitHub", + "legal": "Legal", + "legal_desc": "Kontynuując korzystanie z tego oprogramowania, użytkownik wyraża zgodę na warunki określone w poniższych dokumentach.", + "license": "Licencja", + "lizardbyte_website": "Strona internetowa LizardByte", + "resources": "Zasoby", + "resources_desc": "Zasoby dla Sunshine!", + "third_party_notice": "Powiadomienia strony trzeciej" + }, + "troubleshooting": { + "force_close": "Wymuś zamknięcie", + "force_close_desc": "Jeśli Moonlight skarży się na aktualnie uruchomioną aplikację, wymuszenie jej zamknięcia powinno rozwiązać problem.", + "force_close_error": "Błąd podczas zamykania aplikacji", + "force_close_success": "Aplikacja zamknięta pomyślnie!", + "logs": "Dzienniki", + "logs_desc": "Zobacz dzienniki przesłane przez Sunshine", + "logs_find": "Znajdź...", + "restart_sunshine": "Restart Sunshine", + "restart_sunshine_desc": "Jeśli Sunshine nie działa poprawnie, możesz spróbować uruchomić go ponownie. Spowoduje to zakończenie wszystkich uruchomionych sesji.", + "restart_sunshine_success": "Sunshine uruchamia się ponownie", + "troubleshooting": "Rozwiązywanie problemów", + "unpair_all": "Rozparuj wszystko", + "unpair_all_error": "Błąd podczas rozłączania pary", + "unpair_all_success": "Wszystkie urządzenia rozłączone.", + "unpair_desc": "Usuń sparowane urządzenia. Indywidualnie niesparowane urządzenia z aktywną sesją pozostaną połączone, ale nie będą mogły rozpocząć ani wznowić sesji.", + "unpair_single_no_devices": "Nie ma sparowanych urządzeń.", + "unpair_single_success": "Urządzenia mogą być jednak nadal w aktywnej sesji. Użyj przycisku \"Wymuś zamknięcie\" powyżej, aby zakończyć wszystkie otwarte sesje.", + "unpair_single_unknown": "Nieznany klient", + "unpair_title": "Odparuj urządzenia" + }, + "welcome": { + "confirm_password": "Potwierdź hasło", + "create_creds": "Przed rozpoczęciem pracy należy utworzyć nową nazwę użytkownika i hasło dostępu do interfejsu użytkownika.", + "create_creds_alert": "Poniższe dane uwierzytelniające są potrzebne do uzyskania dostępu do interfejsu użytkownika Sunshine. Zachowaj je w bezpiecznym miejscu, ponieważ nigdy więcej ich nie zobaczysz!", + "greeting": "Witamy w Sunshine!", + "login": "Login", + "welcome_success": "Strona zostanie wkrótce przeładowana, a przeglądarka poprosi o podanie nowych danych uwierzytelniających" + } +} diff --git a/src_assets/common/assets/web/public/assets/locale/pt_BR.json b/src_assets/common/assets/web/public/assets/locale/pt_BR.json new file mode 100644 index 00000000..cf5755a2 --- /dev/null +++ b/src_assets/common/assets/web/public/assets/locale/pt_BR.json @@ -0,0 +1,407 @@ +{ + "_common": { + "apply": "Aplicar", + "auto": "Automático", + "autodetect": "Autodetecção (recomendado)", + "beta": "(beta)", + "cancel": "Cancelar", + "disabled": "Desativado", + "disabled_def": "Desativado (padrão)", + "dismiss": "Dispensar", + "do_cmd": "Comando Do", + "elevated": "Elevado", + "enabled": "Ativado", + "enabled_def": "Ativado (padrão)", + "error": "Erro!", + "note": "Observação:", + "password": "Senha", + "run_as": "Executar como administrador", + "save": "Salvar", + "see_more": "Veja mais", + "success": "Sucesso!", + "undo_cmd": "Comando Desfazer", + "username": "Nome de usuário", + "warning": "Atenção!" + }, + "apps": { + "actions": "Ações", + "add_cmds": "Adicionar comandos", + "add_new": "Adicionar novo", + "app_name": "Nome do aplicativo", + "app_name_desc": "Nome do aplicativo, conforme mostrado no Moonlight", + "applications_desc": "Os aplicativos são atualizados somente quando o Cliente é reiniciado", + "applications_title": "Aplicativos", + "auto_detach": "Continuar a transmissão se o aplicativo for encerrado rapidamente", + "auto_detach_desc": "Isso tentará detectar automaticamente os aplicativos do tipo lançador que fecham rapidamente após iniciar outro programa ou instância deles mesmos. Quando um aplicativo do tipo lançador é detectado, ele é tratado como um aplicativo desvinculado.", + "cmd": "Comando", + "cmd_desc": "O aplicativo principal a ser iniciado. Se estiver em branco, nenhum aplicativo será iniciado.", + "cmd_note": "Se o caminho para o executável do comando contiver espaços, você deverá colocá-lo entre aspas.", + "cmd_prep_desc": "Uma lista de comandos a serem executados antes/depois desse aplicativo. Se algum dos comandos de preparação falhar, a inicialização do aplicativo será abortada.", + "cmd_prep_name": "Preparativos para o comando", + "covers_found": "Capas encontradas", + "delete": "Excluir", + "detached_cmds": "Comandos destacados", + "detached_cmds_add": "Adicionar comando destacado", + "detached_cmds_desc": "Uma lista de comandos a serem executados em segundo plano.", + "detached_cmds_note": "Se o caminho para o executável do comando contiver espaços, você deverá colocá-lo entre aspas.", + "edit": "Editar", + "env_app_id": "ID do aplicativo", + "env_app_name": "Nome do aplicativo", + "env_client_audio_config": "A configuração de áudio solicitada pelo cliente (2.0/5.1/7.1)", + "env_client_enable_sops": "O cliente solicitou a opção de otimizar o jogo para otimizar a transmissão (verdadeiro/falso)", + "env_client_fps": "O FPS solicitado pelo cliente (int)", + "env_client_gcmap": "A máscara de gamepad solicitada, em um formato de conjunto de bits/campo de bits (int)", + "env_client_hdr": "O HDR é ativado pelo cliente (verdadeiro/falso)", + "env_client_height": "A altura solicitada pelo cliente (int)", + "env_client_host_audio": "O cliente solicitou o áudio do host (verdadeiro/falso)", + "env_client_width": "A largura solicitada pelo cliente (int)", + "env_displayplacer_example": "Exemplo - displayplacer para Resolution Automation:", + "env_qres_example": "Exemplo - QRes para automação de resolução:", + "env_qres_path": "caminho do qres", + "env_var_name": "Nome da Var", + "env_vars_about": "Sobre as variáveis de ambiente", + "env_vars_desc": "Todos os comandos obtêm essas variáveis de ambiente por padrão:", + "env_xrandr_example": "Exemplo - Xrandr para automação de resolução:", + "exit_timeout": "Tempo limite de saída", + "exit_timeout_desc": "Número de segundos para aguardar que todos os processos do aplicativo saiam graciosamente quando solicitado a sair. Se não for definido, o padrão é aguardar até 5 segundos. Se for definido como zero ou um valor negativo, o aplicativo será encerrado imediatamente.", + "find_cover": "Encontrar cobertura", + "global_prep_desc": "Ativar/desativar a execução de comandos de preparação global para esse aplicativo.", + "global_prep_name": "Comandos globais de preparação", + "image": "Imagem", + "image_desc": "Caminho do ícone/figura/imagem do aplicativo que será enviado ao cliente. A imagem deve ser um arquivo PNG. Se não for definida, o Sunshine enviará a imagem padrão da caixa.", + "loading": "Carregando...", + "name": "Nome", + "output_desc": "O arquivo em que a saída do comando é armazenada; se não for especificado, a saída será ignorada", + "output_name": "Saída", + "run_as_desc": "Isso pode ser necessário para alguns aplicativos que exigem permissões de administrador para serem executados corretamente.", + "wait_all": "Continuar a transmissão até que todos os processos do aplicativo sejam encerrados", + "wait_all_desc": "Isso continuará a transmissão até que todos os processos iniciados pelo aplicativo tenham sido encerrados. Quando desmarcada, a transmissão será interrompida quando o processo inicial do aplicativo for encerrado, mesmo que outros processos do aplicativo ainda estejam em execução.", + "working_dir": "Diretório de trabalho", + "working_dir_desc": "O diretório de trabalho que deve ser passado para o processo. Por exemplo, alguns aplicativos usam o diretório de trabalho para procurar arquivos de configuração. Se não for definido, o padrão do Sunshine será o diretório pai do comando" + }, + "config": { + "adapter_name": "Nome do adaptador", + "adapter_name_desc_linux_1": "Especificar manualmente uma GPU a ser usada para captura.", + "adapter_name_desc_linux_2": "para encontrar todos os dispositivos compatíveis com VAAPI", + "adapter_name_desc_linux_3": "Substitua ``renderD129`` pelo dispositivo acima para listar o nome e os recursos do dispositivo. Para ser suportado pelo Sunshine, ele precisa ter, no mínimo:", + "adapter_name_desc_windows": "Especificar manualmente uma GPU a ser usada para captura. Se não for definido, a GPU será escolhida automaticamente. É altamente recomendável deixar esse campo em branco para usar a seleção automática de GPU! Observação: essa GPU deve ter uma tela conectada e ligada. Os valores apropriados podem ser encontrados usando o seguinte comando:", + "adapter_name_placeholder_windows": "Série Radeon RX 580", + "add": "Adicionar", + "address_family": "Endereço da família", + "address_family_both": "IPv4+IPv6", + "address_family_desc": "Definir a família de endereços usada pelo Sunshine", + "address_family_ipv4": "Somente IPv4", + "always_send_scancodes": "Sempre enviar códigos de barras", + "always_send_scancodes_desc": "O envio de códigos de barras aumenta a compatibilidade com jogos e aplicativos, mas pode resultar em entrada incorreta do teclado de determinados clientes que não estejam usando um layout de teclado em inglês dos EUA. Ative se a entrada do teclado não estiver funcionando em determinados aplicativos. Desative se as teclas do cliente estiverem gerando a entrada incorreta no host.", + "amd_coder": "Codificador AMF (H264)", + "amd_coder_desc": "Permite que você selecione a codificação de entropia para priorizar a qualidade ou a velocidade de codificação. Somente H.264.", + "amd_enforce_hrd": "Aplicação do decodificador de referência hipotético (HRD) da AMF", + "amd_enforce_hrd_desc": "Aumenta as restrições do controle de taxa para atender aos requisitos do modelo HRD. Isso reduz bastante os excessos de taxa de bits, mas pode causar artefatos de codificação ou qualidade reduzida em determinadas placas.", + "amd_preanalysis": "Pré-análise da AMF", + "amd_preanalysis_desc": "Isso permite a pré-análise de controle de taxa, que pode aumentar a qualidade às custas de uma maior latência de codificação.", + "amd_quality": "Qualidade AMF", + "amd_quality_balanced": "balanced -- balanceado (padrão)", + "amd_quality_desc": "Isso controla o equilíbrio entre a velocidade e a qualidade da codificação.", + "amd_quality_group": "Configurações de qualidade do AMF", + "amd_quality_quality": "qualidade -- prefere qualidade", + "amd_quality_speed": "velocidade -- prefere velocidade", + "amd_rc": "Controle de taxa AMF", + "amd_rc_cbr": "cbr -- taxa de bits constante (recomendado se o HRD estiver ativado)", + "amd_rc_cqp": "cqp -- modo qp constante", + "amd_rc_desc": "Isso controla o método de controle de taxa para garantir que não estamos excedendo a meta de taxa de bits do cliente. O \"cqp\" não é adequado para o direcionamento da taxa de bits, e outras opções além do \"vbr_latency\" dependem da aplicação do HRD para ajudar a restringir os excessos de taxa de bits.", + "amd_rc_group": "Configurações do controle de taxa AMF", + "amd_rc_vbr_latency": "vbr_latency -- taxa de bits variável com restrição de latência (recomendado se o HRD estiver desativado; padrão)", + "amd_rc_vbr_peak": "vbr_peak -- taxa de bits variável com restrição de pico", + "amd_usage": "Uso do AMF", + "amd_usage_desc": "Isso define o perfil de codificação básico. Todas as opções apresentadas abaixo substituirão um subconjunto do perfil de uso, mas há configurações ocultas adicionais aplicadas que não podem ser configuradas em outro lugar.", + "amd_usage_lowlatency": "lowlatency - baixa latência (mais rápida)", + "amd_usage_lowlatency_high_quality": "lowlatency_high_quality - baixa latência, alta qualidade (rápida)", + "amd_usage_transcoding": "transcoding -- transcodificação (mais lento)", + "amd_usage_ultralowlatency": "ultralowlatency - latência ultrabaixa (mais rápida; padrão)", + "amd_usage_webcam": "webcam -- webcam (lenta)", + "amd_vbaq": "Quantização adaptativa baseada em variância AMF (VBAQ)", + "amd_vbaq_desc": "Em geral, o sistema visual humano é menos sensível a artefatos em áreas altamente texturizadas. No modo VBAQ, a variação de pixels é usada para indicar a complexidade das texturas espaciais, permitindo que o codificador aloque mais bits para áreas mais suaves. A ativação desse recurso leva a melhorias na qualidade visual subjetiva com alguns conteúdos.", + "apply_note": "Clique em \"Apply\" (Aplicar) para reiniciar o Sunshine e aplicar as alterações. Isso encerrará todas as sessões em execução.", + "audio_sink": "Dissipador de áudio", + "audio_sink_desc_linux": "O nome do coletor de áudio usado para Loopback de áudio. Se você não especificar essa variável, o pulseaudio selecionará o dispositivo de monitor padrão. Você pode encontrar o nome do coletor de áudio usando qualquer um dos comandos:", + "audio_sink_desc_macos": "O nome do coletor de áudio usado para Loopback de áudio. O Sunshine só pode acessar microfones no macOS devido a limitações do sistema. Para transmitir o áudio do sistema usando o Soundflower ou o BlackHole.", + "audio_sink_desc_windows": "Especificar manualmente um dispositivo de áudio específico para captura. Se não for definido, o dispositivo será escolhido automaticamente. É altamente recomendável deixar esse campo em branco para usar a seleção automática de dispositivos! Se você tiver vários dispositivos de áudio com nomes idênticos, poderá obter o ID do dispositivo usando o seguinte comando:", + "audio_sink_placeholder_macos": "BlackHole 2ch", + "audio_sink_placeholder_windows": "Alto-falantes (dispositivo de áudio de alta definição)", + "av1_mode": "Suporte AV1", + "av1_mode_0": "O Sunshine anunciará o suporte para AV1 com base nos recursos do codificador (recomendado)", + "av1_mode_1": "A Sunshine não fará propaganda do suporte ao AV1", + "av1_mode_2": "A Sunshine anunciará o suporte ao perfil AV1 Main de 8 bits", + "av1_mode_3": "A Sunshine anunciará o suporte aos perfis AV1 Main de 8 e 10 bits (HDR)", + "av1_mode_desc": "Permite que o cliente solicite fluxos de vídeo AV1 Main de 8 ou 10 bits. A codificação do AV1 consome mais CPU, portanto, a ativação dessa opção pode reduzir o desempenho ao usar a codificação de software.", + "back_button_timeout": "Tempo limite de emulação do botão Início/Guia", + "back_button_timeout_desc": "Se o botão Voltar/Selecionar for mantido pressionado pelo número especificado de milissegundos, um pressionamento do botão Início/Guia será emulado. Se definido com um valor < 0 (padrão), manter pressionado o botão Voltar/Selecionar não emulará o botão Início/Guia.", + "capture": "Forçar um método de captura específico", + "capture_desc": "No modo automático, o Sunshine usará o primeiro que funcionar. O NvFBC requer drivers nvidia corrigidos.", + "cert": "Certificado", + "cert_desc": "O certificado usado para a interface do usuário da Web e o emparelhamento do cliente Moonlight. Para melhor compatibilidade, ele deve ter uma chave pública RSA-2048.", + "channels": "Máximo de clientes conectados", + "channels_desc_1": "O Sunshine pode permitir que uma única sessão de streaming seja compartilhada com vários clientes simultaneamente.", + "channels_desc_2": "Alguns codificadores de hardware podem ter limitações que reduzem o desempenho com vários fluxos.", + "coder_cabac": "cabac -- codificação aritmética binária adaptável ao contexto - qualidade superior", + "coder_cavlc": "cavlc -- codificação de comprimento variável adaptável ao contexto - decodificação mais rápida", + "configuration": "Configuração", + "controller": "Ativar entrada do controle de jogo", + "controller_desc": "Permite que os convidados controlem o sistema host com um gamepad/controlador", + "credentials_file": "Arquivo de credenciais", + "credentials_file_desc": "Armazene o nome de usuário/senha separadamente do arquivo de estado do Sunshine.", + "ds4_back_as_touchpad_click": "Mapear Voltar/Selecionar para clicar no touchpad", + "ds4_back_as_touchpad_click_desc": "Ao forçar a emulação DS4, mapeie Back/Select para Touchpad Click", + "encoder": "Forçar um codificador específico", + "encoder_desc": "Force um codificador específico; caso contrário, o Sunshine selecionará a melhor opção disponível. Observação: se você especificar um codificador de hardware no Windows, ele deverá corresponder à GPU em que o monitor está conectado.", + "encoder_software": "Software", + "external_ip": "IP externo", + "external_ip_desc": "Se nenhum endereço IP externo for fornecido, o Sunshine detectará automaticamente o IP externo", + "fec_percentage": "Porcentagem de FEC", + "fec_percentage_desc": "Porcentagem de pacotes de correção de erros por pacote de dados em cada quadro de vídeo. Valores mais altos podem corrigir mais perdas de pacotes na rede, mas ao custo de aumentar o uso da largura de banda.", + "ffmpeg_auto": "auto -- deixa o ffmpeg decidir (padrão)", + "file_apps": "Arquivo de aplicativos", + "file_apps_desc": "O arquivo em que os aplicativos atuais do Sunshine são armazenados.", + "file_state": "Arquivo estadual", + "file_state_desc": "O arquivo em que o estado atual do Sunshine está armazenado", + "gamepad": "Tipo de gamepad emulado", + "gamepad_auto": "Opções de seleção automática", + "gamepad_desc": "Escolha o tipo de gamepad a ser emulado no host", + "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds5": "DS5 (PS5)", + "gamepad_switch": "Nintendo Pro (Switch)", + "gamepad_manual": "Opções manuais do DS4", + "gamepad_x360": "X360 (Xbox 360)", + "gamepad_xone": "XOne (Xbox One)", + "global_prep_cmd": "Preparativos para o comando", + "global_prep_cmd_desc": "Configure uma lista de comandos a serem executados antes ou depois da execução de qualquer aplicativo. Se algum dos comandos de preparação especificados falhar, o processo de inicialização do aplicativo será abortado.", + "hevc_mode": "Suporte a HEVC", + "hevc_mode_0": "A Sunshine anunciará o suporte para HEVC com base nos recursos do codificador (recomendado)", + "hevc_mode_1": "A Sunshine não anunciará suporte para HEVC", + "hevc_mode_2": "A Sunshine anunciará o suporte ao perfil principal HEVC", + "hevc_mode_3": "A Sunshine anunciará o suporte aos perfis HEVC Main e Main10 (HDR)", + "hevc_mode_desc": "Permite que o cliente solicite fluxos de vídeo HEVC Main ou HEVC Main10. A codificação do HEVC consome mais CPU, portanto, ativar essa opção pode reduzir o desempenho ao usar a codificação de software.", + "high_resolution_scrolling": "Suporte à rolagem de alta resolução", + "high_resolution_scrolling_desc": "Quando ativado, o Sunshine transmitirá os eventos de rolagem de alta resolução dos clientes do Moonlight. Isso pode ser útil para desativar aplicativos mais antigos que rolam muito rápido com eventos de rolagem de alta resolução.", + "install_steam_audio_drivers": "Instalar os drivers de áudio do Steam", + "install_steam_audio_drivers_desc": "Se o Steam estiver instalado, isso instalará automaticamente o driver Steam Streaming Speakers para oferecer suporte a som surround 5.1/7.1 e silenciar o áudio do host.", + "key_repeat_delay": "Atraso de repetição de tecla", + "key_repeat_delay_desc": "Controle a velocidade com que as teclas se repetirão. O atraso inicial em milissegundos antes da repetição das teclas.", + "key_repeat_frequency": "Frequência de repetição da tecla", + "key_repeat_frequency_desc": "A frequência com que as teclas se repetem a cada segundo. Essa opção configurável aceita decimais.", + "key_rightalt_to_key_windows": "Mapear a tecla Alt direita para a tecla Windows", + "key_rightalt_to_key_win_desc": "Pode ser que você não consiga enviar a tecla Windows diretamente do Moonlight. Nesses casos, pode ser útil fazer com que o Sunshine pense que a tecla Alt. direita é a tecla Windows", + "keyboard": "Ativar entrada de teclado", + "keyboard_desc": "Permite que os convidados controlem o sistema host com o teclado", + "lan_encryption_mode": "Modo de criptografia de LAN", + "lan_encryption_mode_1": "Ativado para clientes compatíveis", + "lan_encryption_mode_2": "Necessário para todos os clientes", + "lan_encryption_mode_desc": "Isso determina quando a criptografia será usada durante a transmissão pela rede local. A criptografia pode reduzir o desempenho do streaming, principalmente em hosts e clientes menos potentes.", + "locale": "Local", + "locale_desc": "A localidade usada na interface de usuário do Sunshine.", + "log_level": "Nível de registro", + "log_level_0": "Verboso", + "log_level_1": "Depurar", + "log_level_2": "Informações", + "log_level_3": "Advertência", + "log_level_4": "Erro", + "log_level_5": "Fatal", + "log_level_6": "Nenhum", + "log_level_desc": "O nível mínimo de registro impresso na saída padrão", + "log_path": "Caminho do arquivo de registro", + "log_path_desc": "O arquivo em que os registros atuais do Sunshine são armazenados.", + "min_fps_factor": "Fator mínimo de FPS", + "min_fps_factor_desc": "O Sunshine usará esse fator para calcular o tempo mínimo entre os quadros. Aumentar um pouco esse valor pode ajudar na transmissão de conteúdo predominantemente estático. Valores mais altos consumirão mais largura de banda.", + "min_threads": "Contagem mínima de threads da CPU", + "min_threads_desc": "Aumentar o valor reduz ligeiramente a eficiência da codificação, mas a troca geralmente vale a pena para obter o uso de mais núcleos de CPU para codificação. O valor ideal é o menor valor que pode ser codificado de forma confiável nas configurações de streaming desejadas em seu hardware.", + "misc": "Opções diversas", + "motion_as_ds4": "Emular um gamepad DS4 se o gamepad do cliente informar que há sensores de movimento presentes", + "motion_as_ds4_desc": "Se estiver desativado, os sensores de movimento não serão levados em conta durante a seleção do tipo de gamepad.", + "mouse": "Ativar entrada do mouse", + "mouse_desc": "Permite que os convidados controlem o sistema host com o mouse", + "native_pen_touch": "Suporte nativo a caneta/toque", + "native_pen_touch_desc": "Quando ativado, o Sunshine transmitirá os eventos nativos de caneta/toque dos clientes Moonlight. Isso pode ser útil para desativar aplicativos mais antigos sem suporte nativo a caneta/toque.", + "notify_pre_releases": "Notificações de pré-lançamento", + "notify_pre_releases_desc": "Se deseja ser notificado sobre novas versões de pré-lançamento do Sunshine", + "nvenc_h264_cavlc": "Prefira o CAVLC ao CABAC em H.264", + "nvenc_h264_cavlc_desc": "Forma mais simples de codificação de entropia. O CAVLC precisa de cerca de 10% a mais de taxa de bits para obter a mesma qualidade. Só é relevante para dispositivos de decodificação muito antigos.", + "nvenc_latency_over_power": "Prefere uma latência de codificação menor do que a economia de energia", + "nvenc_latency_over_power_desc": "O Sunshine solicita a velocidade máxima do clock da GPU durante o streaming para reduzir a latência da codificação. Não é recomendável desativá-lo, pois isso pode levar a um aumento significativo da latência de codificação.", + "nvenc_opengl_vulkan_on_dxgi": "Apresentar OpenGL/Vulkan sobre o DXGI", + "nvenc_opengl_vulkan_on_dxgi_desc": "O Sunshine não pode capturar programas OpenGL e Vulkan em tela cheia com taxa de quadros total, a menos que eles sejam apresentados na parte superior do DXGI. Essa é uma configuração de todo o sistema que é revertida ao sair do programa Sunshine.", + "nvenc_preset": "Predefinição de desempenho", + "nvenc_preset_1": "(mais rápido, padrão)", + "nvenc_preset_7": "(mais lento)", + "nvenc_preset_desc": "Números mais altos melhoram a compactação (qualidade em uma determinada taxa de bits) ao custo de uma maior latência de codificação. Recomenda-se alterar somente quando limitado pela rede ou pelo decodificador; caso contrário, é possível obter um efeito semelhante aumentando a taxa de bits.", + "nvenc_realtime_hags": "Usar prioridade em tempo real no agendamento de gpu acelerado por hardware", + "nvenc_realtime_hags_desc": "Atualmente, os drivers da NVIDIA podem travar no codificador quando o HAGS está ativado, a prioridade em tempo real é usada e a utilização da VRAM está próxima do máximo. A desativação dessa opção reduz a prioridade para alta, evitando o congelamento ao custo de um desempenho de captura reduzido quando a GPU está muito carregada.", + "nvenc_spatial_aq": "AQ espacial", + "nvenc_spatial_aq_desc": "Atribui valores de QP mais altos a regiões planas do vídeo. Recomenda-se ativá-lo ao fazer streaming com taxas de bits mais baixas.", + "nvenc_spatial_aq_disabled": "Desativado (mais rápido, padrão)", + "nvenc_spatial_aq_enabled": "Ativado (mais lento)", + "nvenc_twopass": "Modo de duas passagens", + "nvenc_twopass_desc": "Adiciona uma passagem de codificação preliminar. Isso permite detectar mais vetores de movimento, distribuir melhor a taxa de bits pelo quadro e aderir mais rigorosamente aos limites de taxa de bits. Não é recomendável desativá-lo, pois isso pode levar a um excesso ocasional de taxa de bits e à subsequente perda de pacotes.", + "nvenc_twopass_disabled": "Desativado (mais rápido, não recomendado)", + "nvenc_twopass_full_res": "Resolução total (mais lenta)", + "nvenc_twopass_quarter_res": "Resolução de um quarto (mais rápida, padrão)", + "nvenc_vbv_increase": "Aumento percentual de VBV/HRD em um único quadro", + "nvenc_vbv_increase_desc": "Por padrão, o sunshine usa VBV/HRD de quadro único, o que significa que não se espera que o tamanho do quadro de vídeo codificado exceda a taxa de bits solicitada dividida pela taxa de quadros solicitada. O relaxamento dessa restrição pode ser benéfico e atuar como taxa de bits variável de baixa latência, mas também pode levar à perda de pacotes se a rede não tiver espaço no buffer para lidar com picos de taxa de bits. O valor máximo aceito é 400, o que corresponde a um limite superior de tamanho de quadro de vídeo codificado 5x maior.", + "origin_web_ui_allowed": "IU da Web de origem permitida", + "origin_web_ui_allowed_desc": "A origem do endereço do ponto de extremidade remoto ao qual não foi negado acesso à interface do usuário da Web", + "origin_web_ui_allowed_lan": "Somente as pessoas na LAN podem acessar a interface do usuário da Web", + "origin_web_ui_allowed_pc": "Somente o localhost pode acessar a interface do usuário da Web", + "origin_web_ui_allowed_wan": "Qualquer pessoa pode acessar a Web UI", + "output_name_desc_unix": "Durante a inicialização do Sunshine, você deverá ver a lista de monitores detectados. Observação: você precisa usar o valor de id dentro do parêntese. Abaixo está um exemplo; a saída real pode ser encontrada na guia Solução de problemas.", + "output_name_desc_windows": "Especifique manualmente um ID de dispositivo de exibição a ser usado para captura. Se não for definido, a tela principal será capturada. Observação: se você especificou uma GPU acima, esse monitor deverá estar conectado a essa GPU. Durante a inicialização do Sunshine, você deverá ver a lista de monitores detectados. Abaixo está um exemplo; a saída real pode ser encontrada na guia Solução de problemas.", + "output_name_unix": "Número de exibição", + "output_name_windows": "Exibir ID do dispositivo", + "ping_timeout": "Tempo limite de ping", + "ping_timeout_desc": "Quanto tempo esperar, em milissegundos, pelos dados do moonlight antes de encerrar o fluxo", + "pkey": "Chave privada", + "pkey_desc": "A chave privada usada para a interface do usuário da Web e o emparelhamento do cliente Moonlight. Para melhor compatibilidade, essa deve ser uma chave privada RSA-2048.", + "port": "Porto", + "port_alert_1": "O Sunshine não pode usar portas abaixo de 1024!", + "port_alert_2": "As portas acima de 65535 não estão disponíveis!", + "port_desc": "Definir a família de portas usadas pelo Sunshine", + "port_http_port_note": "Use essa porta para se conectar ao Moonlight.", + "port_note": "Observação", + "port_port": "Porto", + "port_protocol": "Protocolo", + "port_tcp": "TCP", + "port_udp": "UDP", + "port_warning": "Expor a interface do usuário da Web à Internet é um risco à segurança! Prossiga por sua própria conta e risco!", + "port_web_ui": "UI da Web", + "qp": "Parâmetro de quantização", + "qp_desc": "Alguns dispositivos podem não suportar Constant Bit Rate. Para esses dispositivos, o QP é usado em seu lugar. Um valor mais alto significa mais compactação, mas menos qualidade.", + "qsv_coder": "Codificador QuickSync (H264)", + "qsv_preset": "Predefinição de QuickSync", + "qsv_preset_fast": "rápido (baixa qualidade)", + "qsv_preset_faster": "mais rápido (qualidade inferior)", + "qsv_preset_medium": "médio (padrão)", + "qsv_preset_slow": "lento (boa qualidade)", + "qsv_preset_slower": "mais lento (melhor qualidade)", + "qsv_preset_slowest": "mais lento (melhor qualidade)", + "qsv_preset_veryfast": "mais rápido (qualidade mais baixa)", + "qsv_slow_hevc": "Permitir codificação lenta de HEVC", + "qsv_slow_hevc_desc": "Isso pode permitir a codificação HEVC em GPUs Intel mais antigas, ao custo de maior uso da GPU e pior desempenho.", + "restart_note": "O Sunshine está sendo reiniciado para aplicar as alterações.", + "sunshine_name": "Nome Sunshine", + "sunshine_name_desc": "O nome exibido pelo Moonlight. Se não for especificado, será usado o nome do host do PC", + "sw_preset": "Predefinições de SW", + "sw_preset_desc": "Otimiza o equilíbrio entre a velocidade de codificação (quadros codificados por segundo) e a eficiência da compactação (qualidade por bit no fluxo de bits). O padrão é super-rápido.", + "sw_preset_fast": "rápido", + "sw_preset_faster": "mais rápido", + "sw_preset_medium": "médio", + "sw_preset_slow": "lento", + "sw_preset_slower": "mais lento", + "sw_preset_superfast": "superfast (padrão)", + "sw_preset_ultrafast": "ultrarrápido", + "sw_preset_veryfast": "muito rápido", + "sw_preset_veryslow": "muito lento", + "sw_tune": "SW Tune", + "sw_tune_animation": "animação -- bom para desenhos animados; usa deblocking mais alto e mais quadros de referência", + "sw_tune_desc": "Opções de ajuste, que são aplicadas após a predefinição. O padrão é zerolatência.", + "sw_tune_fastdecode": "fastdecode -- permite uma decodificação mais rápida ao desativar determinados filtros", + "sw_tune_film": "filme - use para conteúdo de filme de alta qualidade; reduz o desbloqueio", + "sw_tune_grain": "granulação -- preserva a estrutura de granulação em material de filme antigo e granulado", + "sw_tune_stillimage": "stillimage -- bom para conteúdo do tipo apresentação de slides", + "sw_tune_zerolatency": "zerolatency -- bom para codificação rápida e streaming de baixa latência (padrão)", + "touchpad_as_ds4": "Emular um gamepad DS4 se o gamepad do cliente informar que há um touchpad presente", + "touchpad_as_ds4_desc": "Se estiver desativado, a presença do touchpad não será levada em conta durante a seleção do tipo de gamepad.", + "upnp": "UPnP", + "upnp_desc": "Configurar automaticamente o encaminhamento de portas para streaming pela Internet", + "vaapi_strict_rc_buffer": "Impor estritamente limites de taxa de bits de quadros para H.264/HEVC em GPUs AMD", + "vaapi_strict_rc_buffer_desc": "A ativação dessa opção pode evitar a queda de quadros na rede durante as mudanças de cena, mas a qualidade do vídeo pode ser reduzida durante o movimento.", + "virtual_sink": "Pia virtual", + "virtual_sink_desc": "Especificar manualmente um dispositivo de áudio virtual a ser usado. Se não for definido, o dispositivo será escolhido automaticamente. É altamente recomendável deixar esse campo em branco para usar a seleção automática de dispositivos!", + "virtual_sink_placeholder": "Alto-falantes de streaming do Steam", + "vt_coder": "Codificador do VideoToolbox", + "vt_realtime": "Codificação em tempo real do VideoToolbox", + "vt_software": "Codificação do software VideoToolbox", + "vt_software_allowed": "Permitido", + "vt_software_forced": "Forçado", + "wan_encryption_mode": "Modo de criptografia WAN", + "wan_encryption_mode_1": "Ativado para clientes compatíveis (padrão)", + "wan_encryption_mode_2": "Necessário para todos os clientes", + "wan_encryption_mode_desc": "Isso determina quando a criptografia será usada durante a transmissão pela Internet. A criptografia pode reduzir o desempenho da transmissão, principalmente em hosts e clientes menos potentes." + }, + "index": { + "description": "O Sunshine é um host de fluxo de jogos auto-hospedado para o Moonlight.", + "download": "Baixar", + "installed_version_not_stable": "Você está executando uma versão de pré-lançamento do Sunshine. É possível que você tenha bugs ou outros problemas. Informe todos os problemas que encontrar. Obrigado por ajudar a tornar o Sunshine um software melhor!", + "loading_latest": "Carregando a versão mais recente...", + "new_pre_release": "Uma nova versão de pré-lançamento está disponível!", + "new_stable": "Uma nova versão estável está disponível!", + "startup_errors": "Atenção! A Sunshine detectou esses erros durante a inicialização. RECOMENDAMOS FORTEMENTE corrigi-los antes da transmissão.", + "version_dirty": "Obrigado por ajudar a tornar o Sunshine um software melhor!", + "version_latest": "Você está executando a versão mais recente do Sunshine", + "welcome": "Olá, Sunshine!" + }, + "navbar": { + "applications": "Aplicativos", + "configuration": "Configuração", + "home": "Início", + "password": "Alterar senha", + "pin": "Pino", + "theme_auto": "Automotivo", + "theme_dark": "Escuro", + "theme_light": "Luz", + "toggle_theme": "Tema", + "troubleshoot": "Solução de problemas" + }, + "password": { + "confirm_password": "Confirmar senha", + "current_creds": "Credenciais atuais", + "new_creds": "Novas credenciais", + "new_username_desc": "Se não for especificado, o nome de usuário não será alterado", + "password_change": "Alteração de senha", + "success_msg": "A senha foi alterada com sucesso! Esta página será recarregada em breve e seu navegador solicitará as novas credenciais." + }, + "pin": { + "device_name": "Nome do dispositivo", + "pair_failure": "Falha no emparelhamento: Verifique se o PIN foi digitado corretamente", + "pair_success": "Sucesso! Por favor, verifique o Moonlight para continuar", + "pin_pairing": "Emparelhamento de PIN", + "send": "Enviar", + "warning_msg": "Certifique-se de ter acesso ao cliente com o qual está fazendo o emparelhamento. Esse software pode dar controle total ao seu computador, portanto, tenha cuidado!" + }, + "resource_card": { + "github_discussions": "Discussões no GitHub", + "legal": "Legal", + "legal_desc": "Ao continuar a usar este software, você concorda com os termos e condições dos documentos a seguir.", + "license": "Licença", + "lizardbyte_website": "Site da LizardByte", + "resources": "Recursos", + "resources_desc": "Recursos para o Sunshine!", + "third_party_notice": "Aviso de terceiros" + }, + "troubleshooting": { + "force_close": "Forçar fechamento", + "force_close_desc": "Se o Moonlight reclamar de um aplicativo em execução, forçar o fechamento do aplicativo deve corrigir o problema.", + "force_close_error": "Erro ao fechar o aplicativo", + "force_close_success": "Aplicativo encerrado com sucesso!", + "logs": "Registros", + "logs_desc": "Veja os registros carregados por Sunshine", + "logs_find": "Encontre...", + "restart_sunshine": "Reiniciar o Sunshine", + "restart_sunshine_desc": "Se o Sunshine não estiver funcionando corretamente, você pode tentar reiniciá-lo. Isso encerrará todas as sessões em execução.", + "restart_sunshine_success": "A luz do sol está reiniciando", + "troubleshooting": "Solução de problemas", + "unpair_all": "Desemparelhar tudo", + "unpair_all_error": "Erro ao desemparelhar", + "unpair_all_success": "Todos os dispositivos não estão emparelhados.", + "unpair_desc": "Remova seus dispositivos emparelhados. Os dispositivos não emparelhados individualmente com uma sessão ativa permanecerão conectados, mas não poderão iniciar ou retomar uma sessão.", + "unpair_single_no_devices": "Não há dispositivos emparelhados.", + "unpair_single_success": "No entanto, o(s) dispositivo(s) ainda pode(m) estar em uma sessão ativa. Use o botão \"Forçar fechamento\" acima para encerrar todas as sessões abertas.", + "unpair_single_unknown": "Cliente desconhecido", + "unpair_title": "Desemparelhar dispositivos" + }, + "welcome": { + "confirm_password": "Confirmar senha", + "create_creds": "Antes de começar, precisamos que você crie um novo nome de usuário e senha para acessar a interface do usuário da Web.", + "create_creds_alert": "As credenciais abaixo são necessárias para acessar a interface de usuário da Web do Sunshine. Mantenha-as em segurança, pois você nunca mais as verá!", + "greeting": "Bem-vindo ao Sunshine!", + "login": "Login", + "welcome_success": "Esta página será recarregada em breve e seu navegador solicitará as novas credenciais" + } +} diff --git a/src_assets/common/assets/web/public/assets/locale/sv.json b/src_assets/common/assets/web/public/assets/locale/sv.json index 109b86c8..04220a2e 100644 --- a/src_assets/common/assets/web/public/assets/locale/sv.json +++ b/src_assets/common/assets/web/public/assets/locale/sv.json @@ -2,13 +2,13 @@ "_common": { "apply": "Tillämpa", "auto": "Automatisk", - "autodetect": "Autodetektera (rekommenderas)", + "autodetect": "Automatdetektion (rekommenderas)", "beta": "(beta)", "cancel": "Avbryt", "disabled": "Inaktiverad", "disabled_def": "Inaktiverad (standard)", "dismiss": "Avfärda", - "do_cmd": "Gör kommando", + "do_cmd": "Kör kommando", "elevated": "Förhöjd", "enabled": "Aktiverad", "enabled_def": "Aktiverad (standard)", @@ -24,59 +24,59 @@ "warning": "Varning!" }, "apps": { - "actions": "Åtgärder", + "actions": "Händelser", "add_cmds": "Lägg till kommandon", "add_new": "Lägg till ny", - "app_name": "Applikationens namn", - "app_name_desc": "Applikationsnamn, som visas på Moonlight", + "app_name": "Applikationsnamn", + "app_name_desc": "Applikationsnamn, som visas i Moonlight", "applications_desc": "Applikationer uppdateras endast när klienten startas om", "applications_title": "Applikationer", "auto_detach": "Fortsätt strömma om programmet avslutas snabbt", - "auto_detach_desc": "Detta kommer att försöka att automatiskt upptäcka launcher-typ appar som stänger snabbt efter att ha startat ett annat program eller instans av sig själva. När en app med launcher-typ upptäcks behandlas den som en fristående app.", + "auto_detach_desc": "Detta kommer att försöka att automatiskt upptäcka \"launcher\"-appar som stänger snabbt efter att ha startat ett annat program eller instans av sig själva. När en app med \"launcher\"-app upptäcks behandlas den som en fristående app.", "cmd": "Kommando", - "cmd_desc": "Huvudansökan att starta. Om den är tom, kommer ingen ansökan att startas.", - "cmd_note": "Om sökvägen till kommandot exekverbart innehåller mellanslag, måste du bifoga det i citattecken.", - "cmd_prep_desc": "En lista över kommandon som ska köras före/efter detta program. Om något av prep-kommandona misslyckas, starta programmet avbryts.", + "cmd_desc": "Huvudprogrammet som startas. Om den är tom, kommer inget program att startas.", + "cmd_note": "Om sökvägen till körbara kommandot innehåller mellanslag, måste du bifoga det i citattecken.", + "cmd_prep_desc": "En lista över kommandon som ska köras före/efter detta program. Om något av för-kommandona misslyckas, avbryts program starten.", "cmd_prep_name": "Kommando förberedelser", "covers_found": "Hittade omslag", "delete": "Radera", "detached_cmds": "Fristående kommandon", "detached_cmds_add": "Lägg till fristående kommando", "detached_cmds_desc": "En lista över kommandon som ska köras i bakgrunden.", - "detached_cmds_note": "Om sökvägen till kommandot exekverbart innehåller mellanslag, måste du bifoga det i citattecken.", + "detached_cmds_note": "Om sökvägen till körbara kommandot innehåller mellanslag, måste du bifoga det i citattecken.", "edit": "Redigera", "env_app_id": "App ID", "env_app_name": "Appens namn", "env_client_audio_config": "Ljudkonfigurationen begärd av klienten (2.0/5.1/7.1)", - "env_client_enable_sops": "Klienten har begärt alternativet att optimera spelet för optimal streaming (true/false)", + "env_client_enable_sops": "Klienten har begärt möjligheten att optimera spelet för optimal steaming\n(sant/falskt)", "env_client_fps": "FPS begärd av klienten (int)", "env_client_gcmap": "Den begärda gamepad masken, i bitset/bitfield format (int)", "env_client_hdr": "HDR är aktiverat av klienten (true/false)", "env_client_height": "Höjd som begärts av kunden (int)", "env_client_host_audio": "Kunden har begärt värdljud (true/false)", "env_client_width": "Bredden begärd av klienten (int)", - "env_displayplacer_example": "Exempel - displayplacer för upplösning Automation:", - "env_qres_example": "Exempel - QRes för upplösning Automation:", + "env_displayplacer_example": "Exempel - en displayplacer för automatiserad upplösning:", + "env_qres_example": "Exempel - QRes för upplösnings automation:", "env_qres_path": "qres sökväg", - "env_var_name": "Var namn", + "env_var_name": "Variabel namn", "env_vars_about": "Om miljövariabler", "env_vars_desc": "Alla kommandon får dessa miljövariabler som standard:", - "env_xrandr_example": "Exempel - Xrandr för upplösning Automation:", - "exit_timeout": "Avsluta Timeout", - "exit_timeout_desc": "Antal sekunder att vänta på att alla app-processer ska avslutas graciöst när det krävs för att avsluta. Om du inte har angett detta är standardvärdet att vänta upp till 5 sekunder. Om satt till noll eller ett negativt värde kommer appen att avslutas omedelbart.", + "env_xrandr_example": "Exempel - Xrandr för upplösnings automation:", + "exit_timeout": "Avbryt Timeout", + "exit_timeout_desc": "Antalet sekunder i väntan på att alla app-processer ska avslutas graciöst när det krävs för att avsluta. Om du inte har angett detta är standardvärdet att vänta upp till 5 sekunder. Om satt till noll eller ett negativt värde kommer appen att avslutas omedelbart.", "find_cover": "Hitta omslag", "global_prep_desc": "Aktivera/Inaktivera exekvering av globala prep kommandon för denna applikation.", "global_prep_name": "Globala prep kommandon", "image": "Bild", - "image_desc": "Applikations ikon/bild/sökväg som kommer att skickas till klienten. Bilden måste vara en PNG-fil. Om den inte är inställd, kommer Sunshine att skicka standardrutans bild.", + "image_desc": "Applikationens ikon/bild/sökväg som kommer att skickas till klienten. Bilden måste vara en PNG-fil. Om den inte är inställd, kommer Sunshine att skicka standardrutans bild.", "loading": "Laddar...", "name": "Namn", "output_desc": "Filen där kommandots utdata lagras, om den inte är angiven, så ignoreras utdata", "output_name": "Utdata", "run_as_desc": "Detta kan vara nödvändigt för vissa program som kräver administratörsbehörighet för att köras korrekt.", - "wait_all": "Fortsätt strömma tills alla appprocesser avslutas", - "wait_all_desc": "Detta fortsätter strömningen tills alla processer som startats av appen har avslutats. När den avmarkeras kommer strömningen att sluta när den initiala appprocessen avslutas, även om andra appprocesser fortfarande är igång.", - "working_dir": "Arbetar katalog", + "wait_all": "Fortsätt strömma tills alla app-processer avslutas", + "wait_all_desc": "Detta fortsätter strömningen tills alla processer som startats av appen har avslutats. När den avmarkeras kommer strömningen att sluta när den initiala app-processen avslutas, även om andra app-processer fortfarande är igång.", + "working_dir": "Arbetskatalog", "working_dir_desc": "Den arbetskatalog som ska skickas till processen. Till exempel använder vissa program arbetskatalogen för att söka efter konfigurationsfiler. Om inte anges, kommer Sunshine standard till den överordnade katalogen i kommandot" }, "config": { @@ -87,14 +87,14 @@ "adapter_name_desc_windows": "Ange manuellt en GPU som ska användas för att fånga. Om du vill avbryta, väljs GPU automatiskt. Vi rekommenderar starkt att du lämnar det här fältet tomt för att använda automatiskt GPU-val! Obs: Denna GPU måste ha en display ansluten och påslagen. Du hittar lämpliga värden med hjälp av följande kommando:", "adapter_name_placeholder_windows": "Radeon RX 580-serien", "add": "Lägg till", - "address_family": "Adress Familj", + "address_family": "Adressfamilj", "address_family_both": "IPv4+IPv6", "address_family_desc": "Ställ in adressfamiljen som används av Sunshine", "address_family_ipv4": "Endast IPv4", - "always_send_scancodes": "Skicka alltid sökkoder", - "always_send_scancodes_desc": "Att skicka skanningskoder förbättrar kompatibiliteten med spel och appar men kan resultera i felaktig tangentbordsinmatning från vissa klienter som inte använder en amerikansk engelsk tangentbordslayout. Aktivera om tangentbordsinmatningen inte fungerar alls i vissa program. Inaktivera om nycklar på klienten genererar fel indata på värden.", + "always_send_scancodes": "Skicka alltid sök koder", + "always_send_scancodes_desc": "Att skicka inmatningskoder förbättrar kompatibiliteten med spel och appar men kan resultera i felaktig tangentbordsinmatning från vissa klienter som inte använder en amerikansk engelsk tangentbordslayout. Aktivera om tangentbordsinmatningen inte fungerar alls i vissa program. Inaktivera om nycklar på klienten genererar fel indata på värden.", "amd_coder": "AMF-kod (H264)", - "amd_coder_desc": "Låter dig välja entropi-kodning för att prioritera kvalitet eller kodningshastighet. H.264 endast.", + "amd_coder_desc": "Låter dig välja entropikodning för att prioritera kvalitet eller kodningshastighet. H.264 endast.", "amd_enforce_hrd": "AMF Hypotetisk referensavkodare (HRD) verkställighet", "amd_enforce_hrd_desc": "Ökar begränsningarna för hastighetskontroll för att uppfylla kraven i HRD-modellen. Detta minskar kraftigt bithastighetsöverflöden, men kan orsaka kodning artefakter eller minskad kvalitet på vissa kort.", "amd_preanalysis": "AMF Föranalys", diff --git a/src_assets/common/assets/web/public/assets/locale/uk.json b/src_assets/common/assets/web/public/assets/locale/uk.json new file mode 100644 index 00000000..033caf83 --- /dev/null +++ b/src_assets/common/assets/web/public/assets/locale/uk.json @@ -0,0 +1,407 @@ +{ + "_common": { + "apply": "Застосувати", + "auto": "Автоматично", + "autodetect": "Автовизначення (рекомендовано)", + "beta": "(бета-версія)", + "cancel": "Скасувати", + "disabled": "Вимкнено", + "disabled_def": "Вимкнено (за замовчуванням)", + "dismiss": "Відхилити", + "do_cmd": "Виконати команду", + "elevated": "Потребуються", + "enabled": "Увімкнено", + "enabled_def": "Увімкнено (за замовчуванням)", + "error": "Помилка!", + "note": "Примітка:", + "password": "Пароль", + "run_as": "Запустити від імені адміністратора", + "save": "Зберегти", + "see_more": "Дивитися більше", + "success": "Успішно!", + "undo_cmd": "Скасувати команду", + "username": "Ім'я користувача", + "warning": "Попередження!" + }, + "apps": { + "actions": "Дії", + "add_cmds": "Додати команди", + "add_new": "Додати новий", + "app_name": "Назва програми", + "app_name_desc": "Назва програми, як показано в Moonlight", + "applications_desc": "Програми оновлюються лише після перезапуску Клієнта", + "applications_title": "Програми", + "auto_detach": "Продовжити стримінг, якщо програма швидко завершується", + "auto_detach_desc": "Ц зробить спробу автоматично виявити програми типу launcher, які швидко закриваються після запуску або запуску іншої програми. Якщо буде виявлено програму типу launcher, її буде розпізнано як окрему програму.", + "cmd": "Команда", + "cmd_desc": "Основна програма для запуску. Якщо поле порожнє, жодна програма не буде запущена.", + "cmd_note": "Якщо шлях до виконуваного файлу команди містить пробіли, ви повинні взяти його в лапки.", + "cmd_prep_desc": "Список команд, які потрібно запустити до/після цього додатка. Якщо будь-яка з підготовчих команд не спрацює, запуск програми буде перервано.", + "cmd_prep_name": "Підготовчі Команди", + "covers_found": "Обкладинки знайдено", + "delete": "Видалити", + "detached_cmds": "Відокремлені команди", + "detached_cmds_add": "Додати окрему команду", + "detached_cmds_desc": "Список команд для запуску у фоновому режимі.", + "detached_cmds_note": "Якщо шлях до виконуваного файлу команди містить пробіли, ви повинні взяти його в лапки.", + "edit": "Редагувати", + "env_app_id": "ID застосунку", + "env_app_name": "Назва програми", + "env_client_audio_config": "Конфігурація аудіо, яку запитує клієнт (2.0/5.1/7.1)", + "env_client_enable_sops": "Клієнт запросив опцію для оптимізації гри та оптимальної якості стримінгу (так/ні)", + "env_client_fps": "FPS, який запитує клієнт (ціле число)", + "env_client_gcmap": "Запитувана маска геймпада у форматі бітового набору/бітового поля (ціле число)", + "env_client_hdr": "HDR увімкнено клієнтом (так/ні)", + "env_client_height": "Висота, яку запитує клієнт (ціле число)", + "env_client_host_audio": "Клієнт запросив аудіо хоста (так/ні)", + "env_client_width": "Ширина, яку запитує клієнт (int)", + "env_displayplacer_example": "Приклад - displayplacer для Автоматизації Роздільної Здатності:", + "env_qres_example": "Приклад - QR-коди для Автоматизації Роздільної Здатності:", + "env_qres_path": "qres шлях", + "env_var_name": "Назва Змінної Середовища", + "env_vars_about": "Про Змінні Середовища", + "env_vars_desc": "Всі команди отримують ці Змінні Середовища за замовчуванням:", + "env_xrandr_example": "Приклад - Xrandr для Автоматизації Роздільної Здатності:", + "exit_timeout": "Тайм-аут виходу", + "exit_timeout_desc": "Кількість секунд, протягом яких всі процеси програми будуть примусово завершені після запиту на вихід. Якщо значення не встановлено, за замовчуванням програма буде чекати до 5 секунд. Якщо встановлено на нуль або від'ємне значення, програму буде негайно завершено.", + "find_cover": "Знайти обкладинку", + "global_prep_desc": "Ввімкнути/Вимкнути виконання глобальних команд Prep для цього застосунку.", + "global_prep_name": "Глобальні команди підготовки", + "image": "Зображення", + "image_desc": "Іконка програми/зображення/шлях до зображення, яке буде надіслано клієнту. Зображення має бути у форматі PNG. Якщо не вказано, Sunshine надішле зображення за замовчуванням.", + "loading": "Завантаження...", + "name": "Ім'я", + "output_desc": "Файл, в якому зберігається вивід команди, якщо його не вказано, то вивід ігнорується", + "output_name": "Виведення", + "run_as_desc": "Це може знадобитися для деяких програм, які потребують дозволів адміністратора для нормального функціонування.", + "wait_all": "Продовжуйте стримінг доти, доки всі процеси програми не завершаться", + "wait_all_desc": "Це продовжить стримінг доти, доки не завершаться всі процеси, запущені програмою. Якщо цей прапорець не ввімкнений, стримінг припиниться після закриття початкової програми, навіть якщо інші процеси програми все ще запущено.", + "working_dir": "Робочий каталог", + "working_dir_desc": "Робочий каталог, який переданий процесу. Наприклад, деякі програми використовують робочий каталог для пошуку файлів конфігурації. Якщо цей параметр не встановлено, Sunshine за замовчуванням буде використовувати батьківський каталог команди" + }, + "config": { + "adapter_name": "Назва Адаптера", + "adapter_name_desc_linux_1": "Вкажіть вручну GPU для захоплення.", + "adapter_name_desc_linux_2": "знайти всі пристрої з підтримкою VAAPI", + "adapter_name_desc_linux_3": "Замініть ``renderD129`` на пристрій зверху, щоб вивести назву та можливості пристрою. Для підтримки Sunshine пристрій повинен мати як мінімум такі параметри:", + "adapter_name_desc_windows": "Вручну вкажіть GPU для захоплення. Якщо GPU не встановлений вручну, то його буде обрано автоматично. Ми наполегливо рекомендуємо залишити це поле порожнім, щоб використовувати автоматичний вибір GPU! Зауважимо, що цей GPU повинен бути ввімкнутим та під'\nєднаним. Допустимі значення можна знайти за допомогою наступної команди:", + "adapter_name_placeholder_windows": "Radeon RX 580 Серії", + "add": "Додати", + "address_family": "Сімейство Адрес", + "address_family_both": "IPv4+IPv6", + "address_family_desc": "Встановити сімейство адрес, що використовується в Sunshine", + "address_family_ipv4": "Тільки IPv4", + "always_send_scancodes": "Завжди Надсилати Скан-коди", + "always_send_scancodes_desc": "Надсилання скан-кодів покращує сумісність з іграми та програмами, але може призвести до некоректного введення з клавіатури деякими клієнтами, які не використовують розкладку клавіатури США. Увімкніть, якщо введення з клавіатури взагалі не працює у певних програмах. Вимкніть, якщо клавіші на клієнті генерують неправильні клавіші на хості.", + "amd_coder": "AMF Coder (H264)", + "amd_coder_desc": "Дозволяє вибрати ентропійне кодування, щоб надати пріоритет якості або швидкості кодування. Тільки H.264.", + "amd_enforce_hrd": "Примусове застосування AMF Hypothetical Reference Decoder (HRD)", + "amd_enforce_hrd_desc": "Збільшує обмеження на керування швидкістю, щоб відповідати вимогам моделі HRD. Це значно зменшує переповнення бітрейту, але може спричинити артефакти кодування або зниження якості на певних GPU.", + "amd_preanalysis": "Попередній аналіз AMF", + "amd_preanalysis_desc": "Це вмикає попередній аналіз контролю швидкості, що може підвищити якість шляхом збільшення затримки кодування.", + "amd_quality": "Якість AMF", + "amd_quality_balanced": "balanced -- збалансований (за замовчуванням)", + "amd_quality_desc": "Це дозволяє контролювати баланс між швидкістю та якістю кодування.", + "amd_quality_group": "Налаштування якості AMF", + "amd_quality_quality": "якість - пріоритизувати якість", + "amd_quality_speed": "швидкість - пріоритизувати швидкість", + "amd_rc": "Контроль Швидкості AMF", + "amd_rc_cbr": "cbr -- постійний бітрейт (рекомендується, якщо увімкнено HRD)", + "amd_rc_cqp": "cqp -- постійний режим qp", + "amd_rc_desc": "Цей параметр контролює метод керування швидкістю, щоб переконатися, що ми не перевищуємо цільовий бітрейт клієнта. 'cqp' не підходить для визначення бітрейту, а інші параметри, окрім 'vbr_latency', залежать від Примусового HRD, щоб допомогти уникнути перезаповнення бітрейту.", + "amd_rc_group": "Налаштування контролю швидкості AMF", + "amd_rc_vbr_latency": "vbr_latency -- обмеженням затримки змінного бітрейту (рекомендується, якщо HRD вимкнено; за замовчуванням)", + "amd_rc_vbr_peak": "vbr_peak -- пікове обмеження змінного бітрейту", + "amd_usage": "Використання AMF", + "amd_usage_desc": "Тут встановлюється базовий профіль кодування. Усі параметри, наведені нижче, перевизначають підмножину профілю використання, але також застосовуються додаткові приховані налаштування, які не можна налаштувати деінде, окрім як тут.", + "amd_usage_lowlatency": "lowlatency - з низькою затримкою (найшвидший)", + "amd_usage_lowlatency_high_quality": "lowlatency_high_quality - низька затримка, висока якість (швидкий)", + "amd_usage_transcoding": "transcoding -- перекодування (найповільніше)", + "amd_usage_ultralowlatency": "ultralowlatency - наднизька затримка (найшвидша; за замовчуванням)", + "amd_usage_webcam": "веб-камера -- веб-камера (повільно)", + "amd_vbaq": "Адаптивне квантування на основі дисперсії AMF (VBAQ)", + "amd_vbaq_desc": "Людський зір зазвичай менш чутливий до артефактів у високотекстурованих областях. У режимі VBAQ дисперсія пікселів використовується для позначення складності просторових текстур, що дозволяє кодеру виділяти більше бітів для більш гладких ділянок. Увімкнення цієї функції призводить до покращення суб'єктивної візуальної якості певного контенту.", + "apply_note": "Натисніть \"Застосувати\", щоб перезапустити Sunshine і застосувати зміни. Це призведе до завершення всіх запущених сеансів.", + "audio_sink": "Пристрій виведення аудіо", + "audio_sink_desc_linux": "Назва пристрою аудіовиводу, що використовується для зациклення звуку. Якщо ви не вкажете цю змінну, pulseaudio вибере пристрій за замовчуванням. Ви можете дізнатися назву пристрою аудіовиводу за допомогою будь-якої з команд:", + "audio_sink_desc_macos": "Назва пристрою аудіовиводу, що використовується для зациклення звуку (Loopback). Sunshine може отримати доступ до мікрофонів лише на macOS через системні обмеження. Для трансляції системного аудіо за допомогою Soundflower або BlackHole.", + "audio_sink_desc_windows": "Вручну вкажіть конкретний аудіопристрій для захоплення. Якщо не вказано, пристрій буде обрано автоматично. Ми наполегливо рекомендуємо залишити це поле порожнім, щоб використовувати автоматичний вибір пристрою! Якщо у вас є кілька аудіопристроїв з однаковими іменами, ви можете отримати ідентифікатор пристрою за допомогою наступної команди:", + "audio_sink_placeholder_macos": "BlackHole 2ch", + "audio_sink_placeholder_windows": "Динаміки (High Definition аудіопристрої)", + "av1_mode": "Підтримка AV1", + "av1_mode_0": "Sunshine пропонуватиме підтримку AV1 на основі можливостей кодерів (рекомендовано)", + "av1_mode_1": "Sunshine не буде пропонувати підтримку AV1", + "av1_mode_2": "Sunshine пропонуватиме підтримку 8-бітового профілю AV1 Main", + "av1_mode_3": "Sunshine пропонуватиме підтримку профілів AV1 Main 8-біт і 10-біт (HDR)", + "av1_mode_desc": "Дозволяє клієнту запитувати основні 8-бітні або 10-бітні відеопотоки AV1. Кодування AV1 вимагає більше ресурсів CPU, тому увімкнення цієї опції може знизити продуктивність при використанні програмного кодування.", + "back_button_timeout": "Тайм-аут емуляції Home/Guide кнопок керування", + "back_button_timeout_desc": "Якщо утримувати кнопки Back/Select протягом вказаної кількості мілісекунд, імітується натискання кнопки Home/Guide. Якщо встановлено значення < 0 (за замовчуванням), утримання кнопки Back/Select не імітуватиме натискання кнопок Home/Guide.", + "capture": "Примусове застосування конкретного методу захоплення (Capture)", + "capture_desc": "У автоматичному режимі Sunshine використовуватиме перший-ліпший драйвер. Для роботи NvFBC потрібні пропатчені драйвери nVidia.", + "cert": "Сертифікат", + "cert_desc": "Сертифікат, який використовується для створення пари між веб UI й клієнтом Moonlight. Для найкращої сумісності він повинен мати відкритий ключ RSA-2048.", + "channels": "Максимальна кількість підключених клієнтів", + "channels_desc_1": "Sunshine може дозволити спільний доступ до однієї стримінгової сесії кільком клієнтам одночасно.", + "channels_desc_2": "Деякі апаратні кодери можуть мати обмеження, які знижують продуктивність при роботі з декількома потоками.", + "coder_cabac": "cabac -- контекстно-адаптивне двійкове арифметичне кодування - вища якість", + "coder_cavlc": "cavlc -- контекстно-адаптивне кодування змінної довжини - швидке декодування", + "configuration": "Налаштування", + "controller": "Увімкнути введення з геймпада", + "controller_desc": "Дозволяє гостям керувати хост-системою за допомогою геймпада / контролера", + "credentials_file": "Файл облікових даних", + "credentials_file_desc": "Зберігайте ім'я користувача/пароль окремо від файлу стану Sunshine.", + "ds4_back_as_touchpad_click": "Призначити клавіші Back/Select на сенсорну клавіатуру", + "ds4_back_as_touchpad_click_desc": "При включеній примусовій емуляції DS4, налаштуйте Back/Select на клацання touchpad'а", + "encoder": "Примусове використання певного кодера", + "encoder_desc": "Примусово використовуйте конкретний кодер, інакше Sunshine обере найкращий з доступних варіантів. Примітка: Якщо ви вказуєте апаратний кодер у Windows, він має відповідати графічному процесору, до якого під'єднано монітор.", + "encoder_software": "Програмне забезпечення", + "external_ip": "Зовнішня IP-адреса", + "external_ip_desc": "Якщо зовнішню IP-адресу не вказано, Sunshine автоматично визначить зовнішню IP-адресу", + "fec_percentage": "Відсоток FEC", + "fec_percentage_desc": "Відсоток пакетів виправлення помилок на кожен пакет даних у кожному відеокадрі. Вищі значення можуть викликати більшу втрату мережевих пакетів, але використовувати збільшену пропускну здатність.", + "ffmpeg_auto": "auto -- дозволити ffmpeg вирішувати (за замовчуванням)", + "file_apps": "Файли програми", + "file_apps_desc": "Файл, у якому зберігаються поточні програми Sunshine.", + "file_state": "Файл стану", + "file_state_desc": "Файл, у якому зберігається поточний стан Sunshine", + "gamepad": "Тип емульованого геймпаду", + "gamepad_auto": "Параметри автоматичного вибору", + "gamepad_desc": "Виберіть тип геймпаду для емуляції на хості", + "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds5": "DS5 (PS5)", + "gamepad_switch": "Nintendo Pro (Switch)", + "gamepad_manual": "Налаштування DS4 вручну", + "gamepad_x360": "X360 (Xbox 360)", + "gamepad_xone": "XOne (Xbox One)", + "global_prep_cmd": "Підготовчі Команди", + "global_prep_cmd_desc": "Налаштуйте список команд, які потрібно виконати до або після запуску будь-якої програми. Якщо будь-яка із зазначених команд підготовки не спрацює, процес запуску програми буде перервано.", + "hevc_mode": "Підтримка HEVC", + "hevc_mode_0": "Sunshine буде рекламувати підтримку HEVC на основі можливостей кодера (рекомендовано)", + "hevc_mode_1": "Sunshine не буде пропонувати підтримку HEVC", + "hevc_mode_2": "Sunshine пропонуватиме підтримку для Основного HEVC профілю", + "hevc_mode_3": "Sunshine пропонуватиме підтримку профілів HEVC Main та Main10 (HDR)", + "hevc_mode_desc": "Дозволяє клієнту запитувати відеопотоки HEVC Main або HEVC Main10. Кодування HEVC вимагає більше ресурсів CPU, тому увімкнення цієї опції може знизити продуктивність при використанні програмного кодування.", + "high_resolution_scrolling": "Підтримка прокрутки з високою роздільною здатністю", + "high_resolution_scrolling_desc": "Якщо увімкнено, Sunshine пропускатиме події прокрутки з високою роздільною здатністю від клієнтів Moonlight. Вимкнення цього може бути корисним для старих програм, які прокручують вміст занадто швидко за допомогою подій прокрутки у високій роздільній здатності.", + "install_steam_audio_drivers": "Встановити звукові Steam драйвери", + "install_steam_audio_drivers_desc": "Якщо Steam інстальовано, це автоматично інсталює драйвер Steam Streaming Speakers для підтримки об'ємного звуку 5.1/7.1 і вимкнення звуку хост-комп'ютера.", + "key_repeat_delay": "Затримка повтору клавіш", + "key_repeat_delay_desc": "Керування швидкістю повторення клавіш. Початкова затримка у мілісекундах перед повторенням натискання.", + "key_repeat_frequency": "Частота повторення клавіш", + "key_repeat_frequency_desc": "Як часто клавіші повторюються щосекунди. Цей параметр підтримує десяткові числа.", + "key_rightalt_to_key_windows": "Зіставити праву клавішу Alt з клавішею Windows", + "key_rightalt_to_key_win_desc": "Може статися так, що ви не зможете надіслати команду клавіші Windows з Moonshine напряму. У таких випадках може бути корисним змусити Sunshine вважати клавішу Alt праворуч клавішею Windows", + "keyboard": "Увімкнути введення з клавіатури", + "keyboard_desc": "Дозволити гостям керувати хост-системою за допомогою клавіатури", + "lan_encryption_mode": "Режим шифрування LAN мережі", + "lan_encryption_mode_1": "Увімкнено для підтримуваних клієнтів", + "lan_encryption_mode_2": "Обов'язкове для всіх клієнтів", + "lan_encryption_mode_desc": "Цей параметр визначає, коли буде використовуватися шифрування під час потокового передавання через локальну мережу. Шифрування може знизити продуктивність потокового передавання, особливо на менш потужних хостах і клієнтах.", + "locale": "Мова", + "locale_desc": "Мова, що використовується для Sunshine UI.", + "log_level": "Рівень Логування", + "log_level_0": "Детально (Verbose)", + "log_level_1": "Режим налагодження (Debug)", + "log_level_2": "Інфо", + "log_level_3": "Попередження", + "log_level_4": "Помилка", + "log_level_5": "Критична помилка", + "log_level_6": "Нічого", + "log_level_desc": "Мінімальний стандартний рівень логування, що виводиться", + "log_path": "Шлях до лог-файлу", + "log_path_desc": "Файл, у якому зберігаються поточні логи Sunshine.", + "min_fps_factor": "Мінімальний FPS Фактор", + "min_fps_factor_desc": "Sunshine використовуватиме цей фактор для обчислення мінімального часу між кадрами. Невелике збільшення цього значення може допомогти при стримінгу переважно статичного контенту. Вищі значення будуть більш негативно впливати на пропускну здатність.", + "min_threads": "Мінімальна кількість потоків CPU", + "min_threads_desc": "Збільшення значення дещо знижує ефективність кодування, але цей компроміс зазвичай вартий того, щоб отримати можливість використовувати більше ядер CPU для кодування. Ідеальне значення - це найменше значення, яке може надійно кодувати за бажаних налаштувань стримінгу на вашому обладнанні.", + "misc": "Інші параметри", + "motion_as_ds4": "Емулювати геймпад DS4, якщо клієнтський геймпад повідомляє про наявність motion датчиків", + "motion_as_ds4_desc": "Якщо вимкнено, motion датчики не враховуватимуться під час вибору типу геймпада.", + "mouse": "Увімкнути введення за допомогою миші", + "mouse_desc": "Дозволяє гостям керувати хост-системою за допомогою миші", + "native_pen_touch": "Вбудована підтримка пера/сенсорного вводу", + "native_pen_touch_desc": "Якщо увімкнено, Sunshine передаватиме події нативного пера/дотику від клієнтів Moonlight. Для старих програм без підтримки нативного пера/дотику може бути корисним вимкнення цього налаштування.", + "notify_pre_releases": "PreRelease Сповіщення", + "notify_pre_releases_desc": "Чи отримувати сповіщення про нові pre-release версії Sunshine", + "nvenc_h264_cavlc": "Надайте перевагу CAVLC над CABAC в H.264", + "nvenc_h264_cavlc_desc": "Простіша форма ентропійного кодування. CAVLC потребує приблизно на 10% більшого бітрейту для такої ж якості. Актуально лише для дуже старих декодерів.", + "nvenc_latency_over_power": "Надайте перевагу меншій затримці кодування над економією енергії", + "nvenc_latency_over_power_desc": "Sunshine запитує максимальну тактову частоту CPU під час стримінгу, щоб зменшити затримку кодування. Вимкнення цього параметра не рекомендується, оскільки це може призвести до значного збільшення затримки кодування.", + "nvenc_opengl_vulkan_on_dxgi": "Відображати OpenGL/Vulkan поверх DXGI", + "nvenc_opengl_vulkan_on_dxgi_desc": "Sunshine не може захоплювати повноекранні програми OpenGL та Vulkan з повною частотою кадрів, якщо вони не присутні поверх DXGI. Це загальносистемне налаштування, яке повертається до початкового стану після завершення роботи програми.", + "nvenc_preset": "Пресет продуктивності", + "nvenc_preset_1": "(найшвидший, за замовчуванням)", + "nvenc_preset_7": "(найповільніше)", + "nvenc_preset_desc": "Більші значення покращують стиснення (якість при заданому бітрейті) ціною збільшення затримки кодування. Рекомендується змінювати тільки тоді, коли це обмежено мережею або декодером, інакше аналогічного ефекту можна досягти збільшенням бітрейту.", + "nvenc_realtime_hags": "Використання пріоритету реального часу у плануванні GPU з апаратним прискоренням", + "nvenc_realtime_hags_desc": "Наразі драйвери NVIDIA можуть зависати в кодері, коли ввімкнено HAGS, використовується пріоритет реального часу та завантаження VRAM близьке до максимального. Вимкнення цієї опції знижує пріоритет до високого, що дозволяє уникнути зависання ціною зниження продуктивності захоплення при високому навантаженні GPU.", + "nvenc_spatial_aq": "Просторове AQ", + "nvenc_spatial_aq_desc": "Призначає вищі значення QP пласким ділянкам відео. Рекомендується вмикати під час стримінгу з низьким бітрейтом.", + "nvenc_spatial_aq_disabled": "Вимкнено (швидше, за замовчуванням)", + "nvenc_spatial_aq_enabled": "Увімкнено (повільніше)", + "nvenc_twopass": "Режим двоетапної перевірки", + "nvenc_twopass_desc": "Додає попередній прохід кодування. Це дозволяє виявити більше векторів руху, краще розподілити бітрейт у кадрі та суворіше дотримуватися лімітів бітрейту. Вимикати його не рекомендується, оскільки це може призвести до випадкового перевищення бітрейту і подальшої втрати пакетів.", + "nvenc_twopass_disabled": "Вимкнено (найшвидше, не рекомендується)", + "nvenc_twopass_full_res": "Повна роздільна здатність (повільніше)", + "nvenc_twopass_quarter_res": "Чверть роздільної здатності (швидше, за замовчуванням)", + "nvenc_vbv_increase": "Збільшення однокадрового VBV/HRD у відсотках", + "nvenc_vbv_increase_desc": "За замовчуванням Sunshine використовує однокадровий VBV/HRD, що означає, що розмір будь-якого закодованого відеокадру не повинен перевищувати запитуваного бітрейту, поділеного на запитувану частоту кадрів. Послаблення цього обмеження може бути корисним і діяти як змінний бітрейт з низькою затримкою, але також може призвести до втрати пакетів, якщо мережа не має достатньої ємності буфера, щоб впоратися зі стрибками бітрейту. Максимально допустиме значення 400, що відповідає 5-кратному збільшенню верхньої межі розміру кодованого відеокадру.", + "origin_web_ui_allowed": "Origin Web UI Дозволено", + "origin_web_ui_allowed_desc": "Походження адреси endpoint'а, якій не заборонено доступ до Web UI", + "origin_web_ui_allowed_lan": "Доступ до Web UI мають лише ті, хто перебуває в LAN мережі", + "origin_web_ui_allowed_pc": "Доступ до Web UI може мати лише localhost", + "origin_web_ui_allowed_wan": "Будь-хто може отримати доступ до Web UI", + "output_name_desc_unix": "Під час запуску Sunshine ви повинні побачити список виявлених дисплеїв. Примітка: Ви маєте використовувати значення ідентифікатора у дужках. Нижче наведено приклад; фактичний вивід можна знайти на вкладці Виправлення неполадок.", + "output_name_desc_windows": "Вручну вкажіть ідентифікатор пристрою для захоплення. Якщо не вказано, буде захоплено основний екран. Примітка: Якщо вище ви вказали GPU, цей дисплей повинен бути підключений до цього ж GPU. Під час запуску Sunshine, ви повинні побачити список виявлених дисплеїв. Нижче наведено приклад, фактичний вивід зображення можна знайти на вкладці Виправлення неполадок.", + "output_name_unix": "Номер дисплея", + "output_name_windows": "Показувати ID пристрою", + "ping_timeout": "Тайм-аут пінгу", + "ping_timeout_desc": "Скільки часу в мілісекундах чекати на дані від Moonlight, перш ніж вимкнути стримінг", + "pkey": "Приватний ключ", + "pkey_desc": "Приватний ключ, який використовується для створення пари між Web UI й клієнтом Moonlight. Для найкращої сумісності це має бути приватний ключ формату RSA-2048.", + "port": "Порт", + "port_alert_1": "Sunshine не може використовувати порти нижче 1024!", + "port_alert_2": "Порти вище 65535 недоступні!", + "port_desc": "Встановити сімейство портів, що використовуються Sunshine", + "port_http_port_note": "Використовуйте цей порт для підключення до Moonlight.", + "port_note": "Нотатка", + "port_port": "Порт", + "port_protocol": "Протокол", + "port_tcp": "TCP", + "port_udp": "UDP", + "port_warning": "Оприлюднення Web UI в Інтернеті є ризикованим щодо безпеки! Дійте на власний страх і ризик!", + "port_web_ui": "Web UI", + "qp": "Параметр квантування", + "qp_desc": "Деякі пристрої можуть не підтримувати постійну швидкість передачі даних. На таких пристроях замість неї використовується QP. Вище значення означає більше стиснення, але нижчу якість.", + "qsv_coder": "QuickSync кодер (H264)", + "qsv_preset": "QuickSync Пресет", + "qsv_preset_fast": "швидко (низька якість)", + "qsv_preset_faster": "швидше (нижча якість)", + "qsv_preset_medium": "середнє (за замовчуванням)", + "qsv_preset_slow": "повільно (хороша якість)", + "qsv_preset_slower": "повільніше (краща якість)", + "qsv_preset_slowest": "найповільніше (найкраща якість)", + "qsv_preset_veryfast": "найшвидше (найнижча якість)", + "qsv_slow_hevc": "Дозволити повільне HEVC кодування", + "qsv_slow_hevc_desc": "Це може дозволити кодування HEVC на старих Intel GPU, але внаслідок більшого використання GPU та гіршої продуктивності.", + "restart_note": "Sunshine перезапускається, щоб застосувати зміни.", + "sunshine_name": "Sunshine ім'я", + "sunshine_name_desc": "Ім'я, яке відображається Moonlight. Якщо не вказано, використовується ім'я хоста комп'ютера", + "sw_preset": "Пресети SW", + "sw_preset_desc": "Оптимізація компромісу між швидкістю кодування (кількість закодованих кадрів за секунду) та ефективністю стиснення (якість на біт у бітовому потоці). За замовчуванням - супершвидко.", + "sw_preset_fast": "швидко", + "sw_preset_faster": "швидше", + "sw_preset_medium": "середнє", + "sw_preset_slow": "повільно", + "sw_preset_slower": "повільніше", + "sw_preset_superfast": "супершвидкий (за замовчуванням)", + "sw_preset_ultrafast": "ультрашвидкий", + "sw_preset_veryfast": "дуже швидкий", + "sw_preset_veryslow": "дуже повільний", + "sw_tune": "ПЗ налаштування", + "sw_tune_animation": "анімація - добре підходить для мультфільмів; використовує вищий рівень деблокування та більше кадрів порівняння", + "sw_tune_desc": "Параметри налаштування, які застосовуються після пресету. За замовчуванням - нульова латентність.", + "sw_tune_fastdecode": "fastdecode -- дозволяє пришвидшити декодування, вимкнувши певні фільтри", + "sw_tune_film": "фільм - використовується для високоякісного кіноконтенту; зменшує деблокування", + "sw_tune_grain": "зернистість - зберігає зернисту структуру в старих, зернистих плівкових матеріалах", + "sw_tune_stillimage": "стоп-кадр - добре підходить для контенту, схожого на слайд-шоу", + "sw_tune_zerolatency": "zerolatency - добре підходить для швидкого кодування та стримінгу з низькою затримкою (за замовчуванням)", + "touchpad_as_ds4": "Емулювати геймпад DS4, якщо клієнтський геймпад повідомляє про наявність touchpad'а", + "touchpad_as_ds4_desc": "Якщо вимкнено, наявність touchpad не враховуватиметься під час вибору типу геймпада.", + "upnp": "UPnP", + "upnp_desc": "Автоматичне налаштування переадресації портів для стримінгу через Інтернет", + "vaapi_strict_rc_buffer": "Суворе дотримання обмежень на бітрейт кадру для H.264/HEVC на GPU від AMD", + "vaapi_strict_rc_buffer_desc": "Увімкнення цієї опції дозволяє уникнути втрати кадрів у мережі під час зміни сцени, але під час руху якість відео може погіршитися.", + "virtual_sink": "Віртуальний пристрій виведення аудіо", + "virtual_sink_desc": "Вручну вкажіть віртуальний аудіопристрій для використання. Якщо не вказано, пристрій буде обрано автоматично. Ми наполегливо рекомендуємо залишити це поле порожнім, щоб використовувати автоматичний вибір пристрою!", + "virtual_sink_placeholder": "Динаміки стримінгу Steam", + "vt_coder": "VideoToolbox Кодер", + "vt_realtime": "Кодування VideoToolbox у реальному часі", + "vt_software": "Кодування ПЗ VideoToolbox", + "vt_software_allowed": "Дозволено", + "vt_software_forced": "Примусово", + "wan_encryption_mode": "Режим шифрування WAN", + "wan_encryption_mode_1": "Увімкнено для підтримуваних клієнтів (за замовчуванням)", + "wan_encryption_mode_2": "Обов'язково для всіх клієнтів", + "wan_encryption_mode_desc": "Цей параметр визначає, коли буде використовуватися шифрування під час потокового передавання через Інтернет. Шифрування може знизити продуктивність потокового стримінгу, особливо на менш потужних хостах і клієнтах." + }, + "index": { + "description": "Sunshine - це самостійний ігровий стримінговий хостинг для Moonlight.", + "download": "Завантажити", + "installed_version_not_stable": "Ви використовуєте попередню версію Sunshine. Ви можете зіткнутися з помилками або іншими проблемами. Будь ласка, повідомляйте про будь-які проблеми, з якими ви зіткнулися. Дякуємо, що допомагаєте зробити Sunshine кращою програмою!", + "loading_latest": "Завантаження останньої версії...", + "new_pre_release": "Доступна нова Pre-Release версія!", + "new_stable": "Доступна нова стабільна версія!", + "startup_errors": "Увага! Sunshine виявив ці помилки під час запуску. Ми НАПОЛЕГЛИВО РЕКОМЕНДУЄМО виправити їх перед стримінгом.", + "version_dirty": "Дякуємо, що допомагаєте зробити Sunshine кращою програмою!", + "version_latest": "Ви використовуєте останню версію Sunshine", + "welcome": "Привіт, Sunshine!" + }, + "navbar": { + "applications": "Застосунки", + "configuration": "Конфігурація", + "home": "Головна", + "password": "Змінити Пароль", + "pin": "Закріпити", + "theme_auto": "Авто", + "theme_dark": "Темна", + "theme_light": "Світла", + "toggle_theme": "Тема", + "troubleshoot": "Усунення неполадок" + }, + "password": { + "confirm_password": "Підтвердити пароль", + "current_creds": "Поточні облікові дані", + "new_creds": "Нові облікові дані", + "new_username_desc": "Якщо не вказано, ім'я користувача не зміниться", + "password_change": "Зміна пароля", + "success_msg": "Пароль успішно змінено! Ця сторінка незабаром перезавантажиться, ваш браузер запитає вас про нові облікові дані." + }, + "pin": { + "device_name": "Назва пристрою", + "pair_failure": "Не вдалося створити пару: Перевірте правильність введення PIN-коду", + "pair_success": "Успішно! Будь ласка, перевірте Moonlight, щоб продовжити", + "pin_pairing": "Сполучення PIN-коду", + "send": "Надіслати", + "warning_msg": "Переконайтеся, що у вас є доступ до клієнта, з яким ви створюєте пару. Це програмне забезпечення може повністю контролювати ваш комп'ютер, тому будьте обережні!" + }, + "resource_card": { + "github_discussions": "Обговорення на GitHub", + "legal": "Юридична інформація", + "legal_desc": "Продовжуючи використовувати це програмне забезпечення, ви погоджуєтеся з умовами та положеннями, викладеними в наступних документах.", + "license": "Ліцензія", + "lizardbyte_website": "Вебсайт LizardByte", + "resources": "Ресурси", + "resources_desc": "Ресурси для Sunshine!", + "third_party_notice": "Сповіщення третім особам" + }, + "troubleshooting": { + "force_close": "Закрити примусово", + "force_close_desc": "Якщо Moonlight скаржиться на запущену програму, примусове закриття програми має вирішити проблему.", + "force_close_error": "Помилка під час закриття програми", + "force_close_success": "Застосунок успішно закрито!", + "logs": "Логи", + "logs_desc": "Перегляньте логи, завантажені Sunshine", + "logs_find": "Пошук...", + "restart_sunshine": "Перезапустити Sunshine", + "restart_sunshine_desc": "Якщо Sunshine не працює належним чином, ви можете спробувати перезапустити його. Це призведе до завершення усіх запущених сеансів.", + "restart_sunshine_success": "Sunshine перезапускається", + "troubleshooting": "Усунення неполадок", + "unpair_all": "Відв'язати всі пари", + "unpair_all_error": "Помилка під час від'єднання пари", + "unpair_all_success": "Усі пристрої не під'єднані.", + "unpair_desc": "Видаліть пов’язані пристрої. Вбудовані пристрої з активною сесією залишаться, але не зможуть почати або відновити сеанс.", + "unpair_single_no_devices": "Немає пов'язаних пристроїв.", + "unpair_single_success": "Однак пристрій(ої) все ще можуть бути в активному сеансі. Використовуйте кнопку \"Примусове закриття\" вище, щоб завершити будь-які відкриті сеанси.", + "unpair_single_unknown": "Невідомий клієнт", + "unpair_title": "Відв'язати пристрої" + }, + "welcome": { + "confirm_password": "Підтвердити пароль", + "create_creds": "Перед початком роботи нам потрібно, щоб ви створили нове ім'я користувача та пароль для доступу до Web UI.", + "create_creds_alert": "Наведені нижче облікові дані необхідні для доступу до Sunshine's Web UI. Зберігайте їх у безпеці, оскільки ви більше ніколи їх не побачите!", + "greeting": "Ласкаво просимо до Sunshine!", + "login": "Авторизація", + "welcome_success": "Ця сторінка незабаром перезавантажиться, ваш браузер попросить вас ввести нові облікові дані" + } +} diff --git a/src_assets/common/assets/web/public/assets/locale/zh.json b/src_assets/common/assets/web/public/assets/locale/zh.json index dcc5b2f1..9cb5bc8d 100644 --- a/src_assets/common/assets/web/public/assets/locale/zh.json +++ b/src_assets/common/assets/web/public/assets/locale/zh.json @@ -166,7 +166,7 @@ "file_state_desc": "Sunshine 保存当前状态的文件", "gamepad": "模拟游戏手柄类型", "gamepad_auto": "自动选择选项", - "gamepad_desc": "选择要在主机上模拟的游戏手柄类型", + "gamepad_desc": "选择要在主机上模拟的游戏手表类型", "gamepad_ds4": "DS4 (PS4)", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", From 151ff8f2b9baf83ede636622cc13228bc6c20f98 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Fri, 3 Jan 2025 17:01:24 -0600 Subject: [PATCH 04/20] fix(capture/macos): fix implicit conversion of NSArray (#3502) --- src/platform/macos/av_video.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platform/macos/av_video.m b/src/platform/macos/av_video.m index 3b58bff5..bb7723bb 100644 --- a/src/platform/macos/av_video.m +++ b/src/platform/macos/av_video.m @@ -32,8 +32,7 @@ } + (NSString *)getDisplayName:(CGDirectDisplayID)displayID { - NSScreen *screens = [NSScreen screens]; - for (NSScreen *screen in screens) { + for (NSScreen *screen in [NSScreen screens]) { if (screen.deviceDescription[@"NSScreenNumber"] == [NSNumber numberWithUnsignedInt:displayID]) { return screen.localizedName; } From 65878af8ea2fa23ada8e4d2b95a1585ccdceac0a Mon Sep 17 00:00:00 2001 From: Lukas Senionis Date: Sat, 4 Jan 2025 15:57:03 +0200 Subject: [PATCH 05/20] fix(docs): use ucrt64 doxygen for msys2 (#3514) --- docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 851b21b1..d2ea0ef3 100644 --- a/docs/building.md +++ b/docs/building.md @@ -83,12 +83,12 @@ pacman -Syu ##### Install dependencies ```bash dependencies=( - "doxygen" # Optional, for docs "git" "mingw-w64-ucrt-x86_64-boost" # Optional "mingw-w64-ucrt-x86_64-cmake" "mingw-w64-ucrt-x86_64-cppwinrt" "mingw-w64-ucrt-x86_64-curl-winssl" + "mingw-w64-ucrt-x86_64-doxygen" # Optional, for docs... better to install official Doxygen "mingw-w64-ucrt-x86_64-graphviz" # Optional, for docs "mingw-w64-ucrt-x86_64-miniupnpc" "mingw-w64-ucrt-x86_64-nlohmann-json" From 2e5c2912332db19d29ee8ccd032a8de4a1f32447 Mon Sep 17 00:00:00 2001 From: Lukas Senionis Date: Sun, 5 Jan 2025 21:43:45 +0200 Subject: [PATCH 06/20] feat(web-ui): replace dropdown menus with checkboxes (#3455) --- src_assets/common/assets/web/Checkbox.vue | 120 +++++++++++ src_assets/common/assets/web/apps.html | 76 +++---- .../assets/web/configs/tabs/AudioVideo.vue | 15 +- .../assets/web/configs/tabs/General.vue | 27 ++- .../common/assets/web/configs/tabs/Inputs.vue | 202 ++++++++---------- .../assets/web/configs/tabs/Network.vue | 15 +- .../tabs/audiovideo/AdapterNameSelector.vue | 2 +- .../tabs/audiovideo/DisplayModesSettings.vue | 11 +- .../configs/tabs/encoders/AmdAmfEncoder.vue | 47 ++-- .../tabs/encoders/IntelQuickSyncEncoder.vue | 16 +- .../tabs/encoders/NvidiaNvencEncoder.vue | 70 +++--- .../configs/tabs/encoders/VAAPIEncoder.vue | 15 +- .../tabs/encoders/VideotoolboxEncoder.vue | 15 +- .../assets/web/public/assets/locale/en.json | 5 +- 14 files changed, 363 insertions(+), 273 deletions(-) create mode 100644 src_assets/common/assets/web/Checkbox.vue diff --git a/src_assets/common/assets/web/Checkbox.vue b/src_assets/common/assets/web/Checkbox.vue new file mode 100644 index 00000000..b94446d3 --- /dev/null +++ b/src_assets/common/assets/web/Checkbox.vue @@ -0,0 +1,120 @@ + + + diff --git a/src_assets/common/assets/web/apps.html b/src_assets/common/assets/web/apps.html index e10ce026..b4669989 100644 --- a/src_assets/common/assets/web/apps.html +++ b/src_assets/common/assets/web/apps.html @@ -116,15 +116,13 @@
{{ $t('apps.output_desc') }}
-
- - -
{{ $t('apps.global_prep_desc') }}
-
+
{{ $t('apps.cmd_prep_desc') }}
@@ -152,12 +150,12 @@ - -
- - -
+ +
-
- - -
{{ $t('apps.run_as_desc') }}
-
+ -
- - -
{{ $t('apps.auto_detach_desc') }}
-
+ -
- - -
{{ $t('apps.wait_all_desc') }}
-
+
@@ -358,11 +360,13 @@ import { createApp } from 'vue' import { initApp } from './init' import Navbar from './Navbar.vue' + import Checkbox from './Checkbox.vue' import { Dropdown } from 'bootstrap/dist/js/bootstrap' const app = createApp({ components: { - Navbar + Navbar, + Checkbox }, data() { return { @@ -415,9 +419,9 @@ if (this.editForm["detached"] === undefined) this.editForm["detached"] = []; if (this.editForm["exclude-global-prep-cmd"] === undefined) - this.editForm["exclude-global-prep-cmd"] = []; + this.editForm["exclude-global-prep-cmd"] = false; if (this.editForm["elevated"] === undefined && this.platform === 'windows') { - this.editForm["elevated"] = []; + this.editForm["elevated"] = false; } if (this.editForm["auto-detach"] === undefined) { this.editForm["auto-detach"] = true; diff --git a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue index 0f18f9a1..430fe1fa 100644 --- a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue +++ b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue @@ -6,6 +6,7 @@ import AdapterNameSelector from './audiovideo/AdapterNameSelector.vue' import DisplayOutputSelector from './audiovideo/DisplayOutputSelector.vue' import DisplayDeviceOptions from "./audiovideo/DisplayDeviceOptions.vue"; import DisplayModesSettings from "./audiovideo/DisplayModesSettings.vue"; +import Checkbox from "../../Checkbox.vue"; const props = defineProps([ 'platform', @@ -54,14 +55,12 @@ const config = ref(props.config)
-
- - -
{{ $t('config.install_steam_audio_drivers_desc') }}
-
+ diff --git a/src_assets/common/assets/web/configs/tabs/General.vue b/src_assets/common/assets/web/configs/tabs/General.vue index 9596e622..d08bec55 100644 --- a/src_assets/common/assets/web/configs/tabs/General.vue +++ b/src_assets/common/assets/web/configs/tabs/General.vue @@ -1,4 +1,5 @@ diff --git a/src_assets/common/assets/web/configs/tabs/encoders/AmdAmfEncoder.vue b/src_assets/common/assets/web/configs/tabs/encoders/AmdAmfEncoder.vue index 209df9a7..1da49999 100644 --- a/src_assets/common/assets/web/configs/tabs/encoders/AmdAmfEncoder.vue +++ b/src_assets/common/assets/web/configs/tabs/encoders/AmdAmfEncoder.vue @@ -1,5 +1,6 @@ - - - - - - - -
- - - - -
-
- -
-
- - -
-
-

- Sunshine is a self-hosted game stream host for Moonlight. Offering low latency, cloud gaming - server capabilities with support for AMD, Intel, and Nvidia GPUs for hardware encoding. Software - encoding is also available. You can connect to Sunshine from any Moonlight client on a variety - of devices. A web UI is provided to allow configuration, and client pairing, from your favorite - web browser. Pair from the local server or any mobile device. -

-
-
- - -
-
-

Features

- -
-
-
-
-
-
- -
-
-
Self-hosted
-

- Run Sunshine on your own hardware. No need to pay monthly fees to a - cloud gaming provider. -

-
-
-
+ +
+
+

Features

+ +
+
+
+
+
+
+
-
-
-
-
-
-
- -
-
-
Moonlight Support
-

- Connect to Sunshine from any Moonlight client. Moonlight is available - for Windows, macOS, Linux, Android, iOS, Xbox, and more. See - clients for more information. -

-
-
-
-
-
-
-
-
-
-
- -
-
-
Hardware Encoding
-

- Sunshine supports AMD, Intel, and Nvidia GPUs for hardware encoding. - Software encoding is also available. -

-
-
-
-
-
-
-
-
-
-
- -
-
-
Low Latency
-

- Sunshine is designed to provide the lowest latency possible to achieve optimal gaming performance. -

-
-
-
-
-
-
-
-
-
-
- -
-
-
Control
-

- Sunshine emulates an Xbox, PlayStation, or Nintendo Switch controller. - Use nearly any controller on your Moonlight client!
- -

    -
  • Nintendo Switch emulation is only available on Linux.
  • -
  • Gamepad emulation is not currently supported on macOS.
  • -
- -

-
-
-
-
-
-
-
-
-
-
- -
-
-
Configurable
-

- Sunshine offers many configuration options to customize your experience. -

-
-
-
+
+
Self-hosted
+

+ Run Sunshine on your own hardware. No need to pay monthly fees to a + cloud gaming provider. +

-
- - -
-
-

Clients

- -
- - -
-
-
-
-
- -
- -
- Official -
-
-
- +
+
+
+
+
+
+ Moonlight +
+
+
Moonlight Support
+

+ Connect to Sunshine from any Moonlight client. Moonlight is available + for Windows, macOS, Linux, Android, iOS, Xbox, and more. See + clients for more information. +

- - -
-
-
-
-
- -
- -
- Official -
-
-
- -
-
- - -
-
-
-
-
- -
-
-
- - iOS - -
-
-
- Official -
-
-
- -
-
- - -
-
-
-
-
- - - - -
-
-
- - QT - -
-
-
- Official -
-
-
- -
-
- - -
-
-
-
-
- -
- -
- Official -
-
-
- -
-
- - -
-
-
-
-
- -
- -
- Community -
-
-
- -
-
- - -
-
-
-
-
- -
- -
- Community -
-
-
- -
-
- - -
-
-
-
-
- -
- -
- Community -
-
-
- -
-
- - -
-
-
-
-
- -
- -
- Community -
-
-
- -
-
- - -
-
-
-
-
- -
- -
- Community -
-
-
- -
-
- -
-
- - -
-
- -
-
-
-
- -
-

Documentation

-

- Read the documentation to learn how to install, use, and configure Sunshine. -

-
-
+
+
+
+
+
+
+
- -
- - -
-
-
-
- -
-

Download

-

- Download Sunshine for your platform. -

-
-
+
+
+
+
+
+
+
+
+
- -
+ + + +
+
+
+
+
+ +
+
+
Control
+

+ Sunshine emulates an Xbox, PlayStation, or Nintendo Switch controller. + Use nearly any controller on your Moonlight client!
+ +

    +
  • Nintendo Switch emulation is only available on Linux.
  • +
  • Gamepad emulation is not currently supported on macOS.
  • +
+ +

+
+
+
+
+
+
+
+
+
+
+ +
+
+
Configurable
+

+ Sunshine offers many configuration options to customize your experience. +

+
+
+
+
+
+ + + + + +
+
+

Clients

+ +
+ + +
+
+
+
+
+ +
+ +
+ Official +
+
+
+
- - - - -
-
-
-
-
-
Support Center
-
Find answers and ask questions.
-
-
-

- The one who knows all the answers has not been asked all the questions. - – Confucius. -

- +
+ Official
+
-
+
-
- -
+ +
+
+
+
+
+ +
+
+
+ + iOS + +
+
+
+ Official +
+
+
+ +
+
- - + +
+
+
+
+
+ + + + +
+
+
+ + QT + +
+
+
+ Official +
+
+
+ +
+
- - + +
+
+
+
+
+ +
+ +
+ Official +
+
+
+ +
+
- - - - - - + +
+
+
+
+
+ +
+ +
+ Community +
+
+
+ +
+
- - + + + - - + +
+
+ +
+
+
+
+ +
+

Documentation

+

+ Read the documentation to learn how to install, use, and configure Sunshine. +

+
+
+
+ +
+
+ + +
+
+
+
+ +
+

Download

+

+ Download Sunshine for your platform. +

+
+
+
+ +
+
+
+
+ + + From df0bc3f82f3af8411b588976928518c517d0fb64 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Tue, 7 Jan 2025 19:18:34 -0500 Subject: [PATCH 08/20] build(packaging/macos)!: remove macports support (#3506) --- .github/workflows/CI.yml | 314 ++++++++------------------------------- docs/getting_started.md | 36 ----- packaging/macos/Portfile | 77 ---------- 3 files changed, 58 insertions(+), 369 deletions(-) delete mode 100644 packaging/macos/Portfile diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5ebf9ed2..ddfb7987 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -547,7 +547,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Fix python + - name: Fix homebrew python if: matrix.os_name == 'macos' && matrix.os_version == '13' run: | rm '/usr/local/bin/2to3' @@ -562,6 +562,12 @@ jobs: rm '/usr/local/bin/python3.12-config' brew install python + - name: Setup python + id: python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + - name: Configure formula run: | # variables for formula @@ -641,6 +647,7 @@ jobs: echo "DISPLAY=${DISPLAY}" >> $GITHUB_ENV - name: Validate Homebrew Formula + id: test if: | matrix.release != true uses: LizardByte/homebrew-release-action@v2024.1115.14934 @@ -652,6 +659,56 @@ jobs: token: ${{ secrets.GH_BOT_TOKEN }} validate: true + - name: Generate gcov report + # any except canceled or skipped + # TODO: fix coverage, no .gcno files are being created + # TODO: .gcno files are supposed to be created next to .o files + if: false + # if: >- + # always() && + # matrix.release != true && + # (steps.test.outcome == 'success' || steps.test.outcome == 'failure') + id: test_report + run: | + # if linux + if [ "${{ runner.os }}" == "Linux" ]; then + prefix="/tmp" + else + prefix="/private/tmp" + fi + + brew_dir=$(find ${prefix} -type d -name "sunshine-*" -maxdepth 1 2>/dev/null) + cp -rf $brew_dir/build/ ./build/ + cd build + ls -Ra + + ${{ steps.python.outputs.python-path }} -m pip install gcovr + ${{ steps.python.outputs.python-path }} -m gcovr . -r ../src \ + --exclude-noncode-lines \ + --exclude-throw-branches \ + --exclude-unreachable-branches \ + --verbose \ + --xml-pretty \ + -o coverage.xml + + - name: Upload coverage + # any except canceled or skipped + # TODO: enable this once coverage report is fixed + if: false + # if: >- + # always() && + # matrix.release != true && + # (steps.test_report.outcome == 'success') && + # startsWith(github.repository, 'LizardByte/') + uses: codecov/codecov-action@v5 + with: + disable_search: true + fail_ci_if_error: true + files: ./build/coverage.xml + flags: ${{ matrix.os_name }}-${{ matrix.os_version }} (Homebrew) + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true + - name: Create/Update GitHub Release if: >- matrix.release && @@ -701,261 +758,6 @@ jobs: token: ${{ secrets.GH_BOT_TOKEN }} validate: false - build_mac_port: - needs: [setup_release] - strategy: - fail-fast: false # false to test all, true to fail entire job if any fail - matrix: - include: - # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories - # while GitHub has larger macOS runners, they are not available for our repos :( - - os_version: "13" - release: true - - os_version: "14" - name: Macports (macOS-${{ matrix.os_version }}) - runs-on: macos-${{ matrix.os_version }} - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Checkout ports - uses: actions/checkout@v4 - with: - repository: macports/macports-ports - fetch-depth: 64 - path: ports - - - name: Checkout mpbb - uses: actions/checkout@v4 - with: - repository: macports/mpbb - path: mpbb - - - name: Setup Dependencies Macports - run: | - # install dependencies using homebrew - brew install cmake - - - name: Setup python - id: python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Configure Portfile - run: | - # variables for Portfile - branch="${{ github.head_ref }}" - commit=${{ needs.setup_release.outputs.release_commit }} - - # check the branch variable - if [ -z "$branch" ] - then - echo "This is a PUSH event" - branch="${{ github.ref_name }}" - build_version=${{ needs.setup_release.outputs.release_tag }} - clone_url=${{ github.event.repository.clone_url }} - else - echo "This is a PR event" - clone_url=${{ github.event.pull_request.head.repo.clone_url }} - fi - echo "Commit: ${commit}" - echo "Clone URL: ${clone_url}" - - mkdir -p build - cmake \ - -B build \ - -S . \ - -DBUILD_VERSION=${build_version} \ - -DGITHUB_BRANCH=${branch} \ - -DGITHUB_COMMIT=${commit} \ - -DGITHUB_CLONE_URL=${clone_url} \ - -DSUNSHINE_CONFIGURE_PORTFILE=ON \ - -DSUNSHINE_CONFIGURE_ONLY=ON - - # copy Portfile to artifacts - mkdir -p artifacts - cp -f ./build/Portfile ./artifacts/ - - # copy Portfile to ports - mkdir -p ./ports/multimedia/Sunshine - cp -f ./build/Portfile ./ports/multimedia/Sunshine/Portfile - - # testing - cat ./artifacts/Portfile - - - name: Bootstrap MacPorts - run: | - . ports/.github/workflows/bootstrap.sh - - # Add getopt, mpbb and the MacPorts paths to $PATH for the subsequent steps. - echo "/opt/mports/bin" >> $GITHUB_PATH - echo "${PWD}/mpbb" >> $GITHUB_PATH - echo "/opt/local/bin" >> $GITHUB_PATH - echo "/opt/local/sbin" >> $GITHUB_PATH - - - name: Run port lint - run: | - port -q lint "Sunshine" - - - name: Build port - env: - subportlist: ${{ steps.subportlist.outputs.subportlist }} - id: build - run: | - subport="Sunshine" - - workdir="/tmp/mpbb/$subport" - mkdir -p "$workdir/logs" - - echo "::group::Installing dependencies" - sudo mpbb \ - --work-dir "$workdir" \ - install-dependencies \ - "$subport" - echo "::endgroup::" - - echo "::group::Installing ${subport}" - sudo mpbb \ - --work-dir "$workdir" \ - install-port \ - --source \ - "$subport" - echo "::endgroup::" - - - name: Build Logs - if: always() - run: | - logfile="/opt/local/var/macports/logs/_Users_runner_work_Sunshine_Sunshine_ports_multimedia_Sunshine/Sunshine/main.log" - cat "$logfile" - sudo mv "${logfile}" "${logfile}.bak" - - - name: Upload Artifacts - if: ${{ matrix.release }} - uses: actions/upload-artifact@v4 - with: - name: sunshine-macports - path: artifacts/ - - - name: Fix permissions - run: | - # https://apple.stackexchange.com/questions/362865/macos-list-apps-authorized-for-full-disk-access - # https://github.com/actions/runner-images/issues/9529 - # https://github.com/actions/runner-images/pull/9530 - - # function to execute sql query for each value - function execute_sql_query { - local value=$1 - local dbPath=$2 - - echo "Executing SQL query for value: $value" - sudo sqlite3 "$dbPath" "INSERT OR IGNORE INTO access VALUES($value);" - } - - # Find all provisioner paths and store them in an array - readarray -t provisioner_paths < <(sudo find /opt /usr -name provisioner) - echo "Provisioner paths: ${provisioner_paths[@]}" - - # Create an empty array - declare -a values=() - - # Loop through the provisioner paths and add them to the values array - for p_path in "${provisioner_paths[@]}"; do - # Adjust the service name and other parameters as needed - values+=("'kTCCServiceAccessibility','${p_path}',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,NULL,1592919552") - values+=("'kTCCServiceScreenCapture','${p_path}',1,2,4,1,NULL,NULL,0,'UNUSED',NULL,0,1687786159") - done - - echo "Values: ${values[@]}" - - if [[ "${{ matrix.os_version }}" == "14" ]]; then - # TCC access table in Sonoma has extra 4 columns: pid, pid_version, boot_uuid, last_reminded - for i in "${!values[@]}"; do - values[$i]="${values[$i]},NULL,NULL,'UNUSED',${values[$i]##*,}" - done - fi - - # system and user databases - dbPaths=( - "/Library/Application Support/com.apple.TCC/TCC.db" - "$HOME/Library/Application Support/com.apple.TCC/TCC.db" - ) - - for value in "${values[@]}"; do - for dbPath in "${dbPaths[@]}"; do - echo "Column names for $dbPath" - echo "-------------------" - sudo sqlite3 "$dbPath" "PRAGMA table_info(access);" - echo "Current permissions for $dbPath" - echo "-------------------" - sudo sqlite3 "$dbPath" "SELECT * FROM access WHERE service='kTCCServiceScreenCapture';" - execute_sql_query "$value" "$dbPath" - echo "Updated permissions for $dbPath" - echo "-------------------" - sudo sqlite3 "$dbPath" "SELECT * FROM access WHERE service='kTCCServiceScreenCapture';" - done - done - - - name: Run tests - id: test - timeout-minutes: 10 - working-directory: - /opt/local/var/macports/build/_Users_runner_work_Sunshine_Sunshine_ports_multimedia_Sunshine/Sunshine/work/build/tests - run: | - sudo ./test_sunshine --gtest_color=yes - - - name: Generate gcov report - # any except canceled or skipped - if: always() && (steps.test.outcome == 'success' || steps.test.outcome == 'failure') - id: test_report - working-directory: - /opt/local/var/macports/build/_Users_runner_work_Sunshine_Sunshine_ports_multimedia_Sunshine/Sunshine/work - run: | - base_dir=$(pwd) - build_dir=${base_dir}/build - - # get the directory name that starts with Sunshine-* - dir=$(ls -d Sunshine-*) - - cd ${build_dir} - ${{ steps.python.outputs.python-path }} -m pip install gcovr - sudo ${{ steps.python.outputs.python-path }} -m gcovr . -r ../${dir}/src \ - --exclude-noncode-lines \ - --exclude-throw-branches \ - --exclude-unreachable-branches \ - --gcov-object-directory $(pwd) \ - --verbose \ - --xml-pretty \ - -o ${{ github.workspace }}/build/coverage.xml - - - name: Upload coverage - # any except canceled or skipped - if: >- - always() && - (steps.test_report.outcome == 'success') && - startsWith(github.repository, 'LizardByte/') - uses: codecov/codecov-action@v5 - with: - disable_search: true - fail_ci_if_error: false # todo: re-enable this when action is fixed - files: ./build/coverage.xml - flags: ${{ runner.os }}-${{ matrix.os_version }} - token: ${{ secrets.CODECOV_TOKEN }} - verbose: true - - - name: Create/Update GitHub Release - if: ${{ needs.setup_release.outputs.publish_release == 'true' }} - uses: LizardByte/create-release-action@v2025.102.13208 - with: - allowUpdates: true - body: ${{ needs.setup_release.outputs.release_body }} - generateReleaseNotes: ${{ needs.setup_release.outputs.release_generate_release_notes }} - name: ${{ needs.setup_release.outputs.release_tag }} - prerelease: true - tag: ${{ needs.setup_release.outputs.release_tag }} - token: ${{ secrets.GH_BOT_TOKEN }} - build_win: name: Windows runs-on: windows-2019 diff --git a/docs/getting_started.md b/docs/getting_started.md index e100d5cc..9281ab6a 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -271,42 +271,6 @@ brew uninstall sunshine @tip{For beta you can replace `sunshine` with `sunshine-beta` in the above commands.} -#### Portfile -This package requires that you have [MacPorts](https://www.macports.org/install.php) installed. - -##### Install -1. Update the Macports sources. - ```bash - sudo nano /opt/local/etc/macports/sources.conf - ``` - - Add this line, replacing your username, below the line that starts with `rsync`. - ```bash - file:///Users//ports - ``` - - `Ctrl+x`, then `Y` to exit and save changes. - -2. Download and install by running the following commands. - ```bash - mkdir -p ~/ports/multimedia/sunshine - cd ~/ports/multimedia/sunshine - curl -OL https://github.com/LizardByte/Sunshine/releases/latest/download/Portfile - cd ~/ports - portindex - sudo port install sunshine - ``` - -##### Install service (optional) -```bash -sudo port load sunshine -``` - -##### Uninstall -```bash -sudo port uninstall sunshine -``` - ### Windows #### Installer (recommended) diff --git a/packaging/macos/Portfile b/packaging/macos/Portfile deleted file mode 100644 index 678f742a..00000000 --- a/packaging/macos/Portfile +++ /dev/null @@ -1,77 +0,0 @@ -# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4 - -# initial PR into macports: https://github.com/macports/macports-ports/pull/15143 - -PortSystem 1.0 -PortGroup cmake 1.1 -PortGroup github 1.0 - -name @PROJECT_NAME@ -version @PROJECT_VERSION@ -revision 0 -categories multimedia emulators games -platforms darwin -license GPL-3 -maintainers @LizardByte -description @PROJECT_DESCRIPTION@ - -# long_description will not be split into multiple lines as it's configured by CMakeLists -long_description @PROJECT_LONG_DESCRIPTION@ -homepage @PROJECT_HOMEPAGE_URL@ -master_sites https://github.com/lizardbyte/sunshine/releases - -compiler.cxx_standard 2017 -fetch.type git - -git.url @GITHUB_CLONE_URL@ -git.branch @GITHUB_COMMIT@ - -post-fetch { - system -W ${worksrcpath} "${git.cmd} submodule update --init --recursive" -} - -# https://guide.macports.org/chunked/reference.dependencies.html -depends_build-append port:doxygen \ - port:graphviz \ - port:npm9 \ - port:pkgconfig - -depends_lib port:curl \ - port:libopus \ - port:miniupnpc - -configure.args -DBOOST_USE_STATIC=ON \ - -DBUILD_WERROR=ON \ - -DCMAKE_INSTALL_PREFIX=${prefix} \ - -DSUNSHINE_ASSETS_DIR=etc/sunshine/assets \ - -DSUNSHINE_PUBLISHER_NAME='LizardByte' \ - -DSUNSHINE_PUBLISHER_WEBSITE='https://app.lizardbyte.dev' \ - -DSUNSHINE_PUBLISHER_ISSUE_URL='https://app.lizardbyte.dev/support' - -configure.env-append BRANCH=@GITHUB_BRANCH@ -configure.env-append BUILD_VERSION=@BUILD_VERSION@ -configure.env-append COMMIT=@GITHUB_COMMIT@ - -startupitem.create yes -startupitem.executable "${prefix}/bin/sunshine" -startupitem.location LaunchDaemons -startupitem.name ${name} -startupitem.netchange yes - -platform darwin { - if { ${os.major} < 20 } { - # See: https://github.com/LizardByte/Sunshine/discussions/117#discussioncomment-2513494 - notes-append "Port is limited to software encoding, when used with macOS releases prior to Big Sur." - } -} - -notes-append "Run @PROJECT_NAME@ by executing 'sunshine ', e.g. 'sunshine ~/sunshine.conf' " -notes-append "The config file will be created if it doesn't exist." -notes-append "It is recommended to set a location for the apps file in the config." -notes-append "See our documentation at 'https://docs.lizardbyte.dev/projects/sunshine/en/v@PROJECT_VERSION@/' for further info." - -test.run yes -test.dir ${build.dir}/tests -test.target "" -test.cmd ./test_sunshine -test.args --gtest_color=yes --gtest_filter=-*HIDTest.*:-*DeathTest.* From 76bea8acb9571117a580146022b405edd1f4e430 Mon Sep 17 00:00:00 2001 From: Lukas Senionis Date: Wed, 8 Jan 2025 03:40:48 +0200 Subject: [PATCH 09/20] feat(display): Configure display device based on user config (#3441) --- docs/configuration.md | 241 +++++++ src/audio.cpp | 34 +- src/audio.h | 44 ++ src/config.cpp | 85 ++- src/config.h | 43 +- src/confighttp.cpp | 18 + src/display_device.cpp | 586 +++++++++++++++++- src/display_device.h | 126 +++- src/main.cpp | 2 +- src/nvhttp.cpp | 59 +- src/platform/common.h | 8 + src/platform/linux/audio.cpp | 6 + src/platform/macos/microphone.mm | 6 + src/platform/windows/audio.cpp | 7 + src/process.cpp | 8 +- src/stream.cpp | 9 +- src_assets/common/assets/web/config.html | 8 + .../assets/web/configs/tabs/AudioVideo.vue | 5 + .../tabs/audiovideo/DisplayDeviceOptions.vue | 146 ++++- .../assets/web/public/assets/locale/en.json | 28 + .../common/assets/web/troubleshooting.html | 40 ++ tests/unit/test_display_device.cpp | 276 +++++++++ 22 files changed, 1690 insertions(+), 95 deletions(-) create mode 100644 tests/unit/test_display_device.cpp diff --git a/docs/configuration.md b/docs/configuration.md index 3b558071..139b7501 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -958,6 +958,247 @@ editing the `conf` file in a text editor. Use the examples as reference. +### dd_configuration_option + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Description + Perform mandatory verification and additional configuration for the display device. + @note{Applies to Windows only.} +
Default@code{}verify_only@endcode
Example@code{} + dd_configuration_option = ensure_only_display + @endcode
ChoicesdisabledPerform no additional configuration (disables all `dd_` configuration options).
verify_onlyVerify that display is active only (this is a mandatory step without any extra steps to verify display state).
ensure_activeActivate the display if it's currently inactive.
ensure_primaryActivate the display if it's currently inactive and make it primary.
ensure_only_displayActivate the display if it's currently inactive and disable all others.
+ +### dd_resolution_option + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Description + Perform additional resolution configuration for the display device. + @note{"Optimize game settings" must be enabled in Moonlight for this option to work.} + @note{Applies to Windows only.} +
Default@code{}auto@endcode
Example@code{} + dd_resolution_option = manual + @endcode
ChoicesdisabledPerform no additional configuration.
autoChange resolution to the requested resolution from the client.
manualChange resolution to the user specified one (set via [dd_manual_resolution](#dd_manual_resolution)).
+ +### dd_manual_resolution + + + + + + + + + + + + + + +
Description + Specify manual resolution to be used. + @note{[dd_resolution_option](#dd_resolution_option) must be set to `manual`} + @note{Applies to Windows only.} +
Defaultn/a
Example@code{} + dd_manual_resolution = 1920x1080 + @endcode
+ +### dd_refresh_rate_option + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Description + Perform additional refresh rate configuration for the display device. + @note{Applies to Windows only.} +
Default@code{}auto@endcode
Example@code{} + dd_refresh_rate_option = manual + @endcode
ChoicesdisabledPerform no additional configuration.
autoChange refresh rate to the requested FPS value from the client.
manualChange refresh rate to the user specified one (set via [dd_manual_refresh_rate](#dd_manual_refresh_rate)).
+ +### dd_manual_refresh_rate + + + + + + + + + + + + + + +
Description + Specify manual refresh rate to be used. + @note{[dd_refresh_rate_option](#dd_refresh_rate_option) must be set to `manual`} + @note{Applies to Windows only.} +
Defaultn/a
Example@code{} + dd_manual_resolution = 120 + dd_manual_resolution = 59.95 + @endcode
+ +### dd_hdr_option + + + + + + + + + + + + + + + + + + + + + + + +
Description + Perform additional HDR configuration for the display device. + @note{Applies to Windows only.} +
Default@code{}auto@endcode
Example@code{} + dd_hdr_option = disabled + @endcode
ChoicesdisabledPerform no additional configuration.
autoChange HDR to the requested state from the client if the display supports it.
+ +### dd_wa_hdr_toggle + + + + + + + + + + + + + + +
Description + 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.} +
Default@code{} + disabled + @endcode
Example@code{} + dd_wa_hdr_toggle = enabled + @endcode
+ +### dd_config_revert_delay + + + + + + + + + + + + + + +
Description + 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. + @note{Applies to Windows only.} +
Default@code{}3000@endcode
Example@code{} + dd_config_revert_delay = 1500 + @endcode
+ ### min_fps_factor diff --git a/src/audio.cpp b/src/audio.cpp index b24ae613..82b1ec37 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -20,16 +20,6 @@ namespace audio { using opus_t = util::safe_ptr; using sample_queue_t = std::shared_ptr>>; - struct audio_ctx_t { - // We want to change the sink for the first stream only - std::unique_ptr sink_flag; - - std::unique_ptr control; - - bool restore_sink; - platf::sink_t sink; - }; - static int start_audio_control(audio_ctx_t &ctx); static void @@ -95,8 +85,6 @@ namespace audio { }, }; - auto control_shared = safe::make_shared(start_audio_control, stop_audio_control); - void encodeThread(sample_queue_t samples, config_t config, void *channel_data) { auto packets = mail::man->queue(mail::audio_packets); @@ -149,7 +137,7 @@ namespace audio { apply_surround_params(stream, config.customStreamParams); } - auto ref = control_shared.ref(); + auto ref = get_audio_ctx_ref(); if (!ref) { return; } @@ -255,6 +243,26 @@ namespace audio { } } + audio_ctx_ref_t + get_audio_ctx_ref() { + static auto control_shared { safe::make_shared(start_audio_control, stop_audio_control) }; + return control_shared.ref(); + } + + bool + is_audio_ctx_sink_available(const audio_ctx_t &ctx) { + if (!ctx.control) { + return false; + } + + const std::string &sink = ctx.sink.host.empty() ? config::audio.sink : ctx.sink.host; + if (sink.empty()) { + return false; + } + + return ctx.control->is_sink_available(sink); + } + int map_stream(int channels, bool quality) { int shift = quality ? 1 : 0; diff --git a/src/audio.h b/src/audio.h index 208a5775..927dfdef 100644 --- a/src/audio.h +++ b/src/audio.h @@ -4,6 +4,8 @@ */ #pragma once +// local includes +#include "platform/common.h" #include "thread_safe.h" #include "utility.h" @@ -55,8 +57,50 @@ namespace audio { std::bitset flags; }; + struct audio_ctx_t { + // We want to change the sink for the first stream only + std::unique_ptr sink_flag; + + std::unique_ptr control; + + bool restore_sink; + platf::sink_t sink; + }; + using buffer_t = util::buffer_t; using packet_t = std::pair; + using audio_ctx_ref_t = safe::shared_t::ptr_t; + void capture(safe::mail_t mail, config_t config, void *channel_data); + + /** + * @brief Get the reference to the audio context. + * @returns A shared pointer reference to audio context. + * @note Aside from the configuration purposes, it can be used to extend the + * audio sink lifetime to capture sink earlier and restore it later. + * + * @examples + * audio_ctx_ref_t audio = get_audio_ctx_ref() + * @examples_end + */ + audio_ctx_ref_t + get_audio_ctx_ref(); + + /** + * @brief Check if the audio sink held by audio context is available. + * @returns True if available (and can probably be restored), false otherwise. + * @note Useful for delaying the release of audio context shared pointer (which + * tries to restore original sink). + * + * @examples + * audio_ctx_ref_t audio = get_audio_ctx_ref() + * if (audio.get()) { + * return is_audio_ctx_sink_available(*audio.get()); + * } + * return false; + * @examples_end + */ + bool + is_audio_ctx_sink_available(const audio_ctx_t &ctx); } // namespace audio diff --git a/src/config.cpp b/src/config.cpp index 32b43127..43527f45 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -328,13 +328,65 @@ namespace config { } } // namespace sw + namespace dd { + video_t::dd_t::config_option_e + config_option_from_view(const std::string_view value) { +#define _CONVERT_(x) \ + if (value == #x##sv) return video_t::dd_t::config_option_e::x + _CONVERT_(disabled); + _CONVERT_(verify_only); + _CONVERT_(ensure_active); + _CONVERT_(ensure_primary); + _CONVERT_(ensure_only_display); +#undef _CONVERT_ + return video_t::dd_t::config_option_e::disabled; // Default to this if value is invalid + } + + video_t::dd_t::resolution_option_e + resolution_option_from_view(const std::string_view value) { +#define _CONVERT_2_ARG_(str, val) \ + if (value == #str##sv) return video_t::dd_t::resolution_option_e::val +#define _CONVERT_(x) _CONVERT_2_ARG_(x, x) + _CONVERT_(disabled); + _CONVERT_2_ARG_(auto, automatic); + _CONVERT_(manual); +#undef _CONVERT_ +#undef _CONVERT_2_ARG_ + return video_t::dd_t::resolution_option_e::disabled; // Default to this if value is invalid + } + + video_t::dd_t::refresh_rate_option_e + refresh_rate_option_from_view(const std::string_view value) { +#define _CONVERT_2_ARG_(str, val) \ + if (value == #str##sv) return video_t::dd_t::refresh_rate_option_e::val +#define _CONVERT_(x) _CONVERT_2_ARG_(x, x) + _CONVERT_(disabled); + _CONVERT_2_ARG_(auto, automatic); + _CONVERT_(manual); +#undef _CONVERT_ +#undef _CONVERT_2_ARG_ + return video_t::dd_t::refresh_rate_option_e::disabled; // Default to this if value is invalid + } + + video_t::dd_t::hdr_option_e + hdr_option_from_view(const std::string_view value) { +#define _CONVERT_2_ARG_(str, val) \ + if (value == #str##sv) return video_t::dd_t::hdr_option_e::val +#define _CONVERT_(x) _CONVERT_2_ARG_(x, x) + _CONVERT_(disabled); + _CONVERT_2_ARG_(auto, automatic); +#undef _CONVERT_ +#undef _CONVERT_2_ARG_ + return video_t::dd_t::hdr_option_e::disabled; // Default to this if value is invalid + } + } // namespace dd + video_t video { 28, // qp 0, // hevc_mode 0, // av1_mode - 1, // min_fps_factor 2, // min_threads { "superfast"s, // preset @@ -385,6 +437,19 @@ namespace config { {}, // encoder {}, // adapter_name {}, // output_name + + { + 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 + {}, // manual_refresh_rate + video_t::dd_t::hdr_option_e::automatic, // hdr_option + 3s, // config_revert_delay + {} // wa + }, // display_device + + 1 // min_fps_factor }; audio_t audio { @@ -952,9 +1017,9 @@ namespace config { } int_f(vars, "qp", video.qp); - int_f(vars, "min_threads", video.min_threads); int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 }); int_between_f(vars, "av1_mode", video.av1_mode, { 0, 3 }); + int_f(vars, "min_threads", video.min_threads); string_f(vars, "sw_preset", video.sw.sw_preset); if (!video.sw.sw_preset.empty()) { video.sw.svtav1_preset = sw::svtav1_preset_from_view(video.sw.sw_preset); @@ -1024,6 +1089,22 @@ namespace config { string_f(vars, "encoder", video.encoder); string_f(vars, "adapter_name", video.adapter_name); string_f(vars, "output_name", video.output_name); + + generic_f(vars, "dd_configuration_option", video.dd.configuration_option, dd::config_option_from_view); + generic_f(vars, "dd_resolution_option", video.dd.resolution_option, dd::resolution_option_from_view); + string_f(vars, "dd_manual_resolution", video.dd.manual_resolution); + generic_f(vars, "dd_refresh_rate_option", video.dd.refresh_rate_option, dd::refresh_rate_option_from_view); + string_f(vars, "dd_manual_refresh_rate", video.dd.manual_refresh_rate); + generic_f(vars, "dd_hdr_option", video.dd.hdr_option, dd::hdr_option_from_view); + { + int value = -1; + int_between_f(vars, "dd_config_revert_delay", value, { 0, std::numeric_limits::max() }); + if (value >= 0) { + video.dd.config_revert_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 }); path_f(vars, "pkey", nvhttp.pkey); diff --git a/src/config.h b/src/config.h index 891a4079..e481a1e7 100644 --- a/src/config.h +++ b/src/config.h @@ -21,7 +21,6 @@ namespace config { int hevc_mode; int av1_mode; - int min_fps_factor; // Minimum fps target, determines minimum frame time int min_threads; // Minimum number of threads/slices for CPU encoding struct { std::string sw_preset; @@ -79,6 +78,48 @@ namespace config { std::string encoder; std::string adapter_name; std::string output_name; + + struct dd_t { + struct workarounds_t { + bool hdr_toggle; ///< Specify whether to apply HDR high-contrast color workaround. + }; + + enum class config_option_e { + disabled, ///< Disable the configuration for the device. + verify_only, ///< @seealso{display_device::SingleDisplayConfiguration::DevicePreparation} + ensure_active, ///< @seealso{display_device::SingleDisplayConfiguration::DevicePreparation} + ensure_primary, ///< @seealso{display_device::SingleDisplayConfiguration::DevicePreparation} + ensure_only_display ///< @seealso{display_device::SingleDisplayConfiguration::DevicePreparation} + }; + + enum class resolution_option_e { + disabled, ///< Do not change resolution. + automatic, ///< Change resolution and use the one received from Moonlight. + manual ///< Change resolution and use the manually provided one. + }; + + enum class refresh_rate_option_e { + disabled, ///< Do not change refresh rate. + automatic, ///< Change refresh rate and use the one received from Moonlight. + manual ///< Change refresh rate and use the manually provided one. + }; + + enum class hdr_option_e { + disabled, ///< Do not change HDR settings. + automatic ///< Change HDR settings and use the state requested by Moonlight. + }; + + config_option_e configuration_option; + resolution_option_e resolution_option; + std::string manual_resolution; ///< Manual resolution in case `resolution_option == resolution_option_e::manual`. + refresh_rate_option_e refresh_rate_option; + std::string manual_refresh_rate; ///< Manual refresh rate in case `refresh_rate_option == refresh_rate_option_e::manual`. + hdr_option_e hdr_option; + std::chrono::milliseconds config_revert_delay; ///< Time to wait until settings are reverted (after stream ends/app exists). + workarounds_t wa; + } dd; + + int min_fps_factor; // Minimum fps target, determines minimum frame time }; struct audio_t { diff --git a/src/confighttp.cpp b/src/confighttp.cpp index 756a4688..9b8570e0 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -28,6 +28,7 @@ #include "config.h" #include "confighttp.h" #include "crypto.h" +#include "display_device.h" #include "file_handler.h" #include "globals.h" #include "httpcommon.h" @@ -734,6 +735,22 @@ namespace confighttp { * } * @endcode */ + void + resetDisplayDevicePersistence(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) return; + + print_req(request); + + pt::ptree outputTree; + auto g = util::fail_guard([&outputTree, &response]() { + std::ostringstream data; + pt::write_json(data, outputTree); + response->write(data.str()); + }); + + outputTree.put("status", display_device::reset_persistence()); + } + void savePassword(resp_https_t response, req_https_t request) { if (!config::sunshine.username.empty() && !authenticate(response, request)) return; @@ -976,6 +993,7 @@ namespace confighttp { server.resource["^/api/config$"]["POST"] = saveConfig; server.resource["^/api/configLocale$"]["GET"] = getLocale; server.resource["^/api/restart$"]["POST"] = restart; + server.resource["^/api/reset-display-device-persistence$"]["POST"] = resetDisplayDevicePersistence; server.resource["^/api/password$"]["POST"] = savePassword; server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp; server.resource["^/api/clients/unpair-all$"]["POST"] = unpairAll; diff --git a/src/display_device.cpp b/src/display_device.cpp index f273104b..a9a21b6a 100644 --- a/src/display_device.cpp +++ b/src/display_device.cpp @@ -6,12 +6,19 @@ #include "display_device.h" // lib includes +#include +#include +#include #include #include #include +#include +#include // local includes +#include "audio.h" #include "platform/common.h" +#include "rtsp.h" // platform-specific includes #ifdef _WIN32 @@ -22,52 +29,508 @@ namespace display_device { namespace { + constexpr std::chrono::milliseconds DEFAULT_RETRY_INTERVAL { 5000 }; + /** - * @brief A global for the settings manager interface whose lifetime is managed by `display_device::init()`. + * @brief A global for the settings manager interface and other settings whose lifetime is managed by `display_device::init(...)`. */ - std::unique_ptr> SM_INSTANCE; + struct { + std::mutex mutex {}; + std::chrono::milliseconds config_revert_delay { 0 }; + std::unique_ptr> sm_instance { nullptr }; + } DD_DATA; + + /** + * @brief Helper class for capturing audio context when the API demands it. + * + * The capture is needed to be done in case some of the displays are going + * to be deactivated before the stream starts. In this case the audio context + * will be captured for this display and can be restored once it is turned back. + */ + class sunshine_audio_context_t: public AudioContextInterface { + public: + [[nodiscard]] bool + capture() override { + return context_scheduler.execute([](auto &audio_context) { + // Explicitly releasing the context first in case it was not release yet so that it can be potentially cleaned up. + audio_context = boost::none; + audio_context = audio_context_t {}; + + // Always say that we have captured it successfully as otherwise the settings change procedure will be aborted. + return true; + }); + } + + [[nodiscard]] bool + isCaptured() const override { + return context_scheduler.execute([](const auto &audio_context) { + if (audio_context) { + // In case we still have context we need to check whether it was released or not. + // If it was released we can pretend that we no longer have it as it will be immediately cleaned up in `capture` method before we acquire new context. + return !audio_context->released; + } + + return false; + }); + } + + void + release() override { + context_scheduler.schedule([](auto &audio_context, auto &stop_token) { + if (audio_context) { + audio_context->released = true; + + const auto *audio_ctx_ptr = audio_context->audio_ctx_ref.get(); + if (audio_ctx_ptr && !audio::is_audio_ctx_sink_available(*audio_ctx_ptr) && audio_context->retry_counter > 0) { + // It is possible that the audio sink is not immediately available after the display is turned on. + // Therefore, we will hold on to the audio context a little longer, until it is either available + // or we time out. + --audio_context->retry_counter; + return; + } + } + + audio_context = boost::none; + stop_token.requestStop(); + }, + SchedulerOptions { .m_sleep_durations = { 2s } }); + } + + private: + struct audio_context_t { + /** + * @brief A reference to the audio context that will automatically extend the audio session. + * @note It is auto-initialized here for convenience. + */ + decltype(audio::get_audio_ctx_ref()) audio_ctx_ref { audio::get_audio_ctx_ref() }; + + /** + * @brief Will be set to true if the capture was released, but we still have to keep the context around, because the device is not available. + */ + bool released { false }; + + /** + * @brief How many times to check if the audio sink is available before giving up. + */ + int retry_counter { 15 }; + }; + + RetryScheduler> context_scheduler { std::make_unique>(boost::none) }; + }; + + /** + * @breif Convert string to unsigned int. + * @note For random reason there is std::stoi, but not std::stou... + * @param value String to be converted + * @return Parsed unsigned integer. + */ + unsigned int + stou(const std::string &value) { + unsigned long result { std::stoul(value) }; + if (result > std::numeric_limits::max()) { + throw std::out_of_range("stou"); + } + return result; + } + + /** + * @brief Parse resolution value from the string. + * @param input String to be parsed. + * @param output Reference to output variable to fill in. + * @returns True on successful parsing (empty string allowed), false otherwise. + * + * @examples + * std::optional resolution; + * if (parse_resolution_string("1920x1080", resolution)) { + * if (resolution) { + * BOOST_LOG(info) << "Value was specified"; + * } + * else { + * BOOST_LOG(info) << "Value was empty"; + * } + * } + * @examples_end + */ + bool + parse_resolution_string(const std::string &input, std::optional &output) { + const std::string trimmed_input { boost::algorithm::trim_copy(input) }; + const std::regex resolution_regex { R"(^(\d+)x(\d+)$)" }; + + if (std::smatch match; std::regex_match(trimmed_input, match, resolution_regex)) { + try { + output = Resolution { + stou(match[1].str()), + stou(match[2].str()) + }; + return true; + } + catch (const std::out_of_range &) { + BOOST_LOG(error) << "Failed to parse resolution string " << trimmed_input << " (number out of range)."; + } + catch (const std::exception &err) { + BOOST_LOG(error) << "Failed to parse resolution string " << trimmed_input << ":\n" + << err.what(); + } + } + else { + if (trimmed_input.empty()) { + output = std::nullopt; + return true; + } + + BOOST_LOG(error) << "Failed to parse resolution string " << trimmed_input << R"(. It must match a "1920x1080" pattern!)"; + } + + return false; + } + + /** + * @brief Parse refresh rate value from the string. + * @param input String to be parsed. + * @param output Reference to output variable to fill in. + * @returns True on successful parsing (empty string allowed), false otherwise. + * + * @examples + * std::optional refresh_rate; + * if (parse_refresh_rate_string("59.95", refresh_rate)) { + * if (refresh_rate) { + * BOOST_LOG(info) << "Value was specified"; + * } + * else { + * BOOST_LOG(info) << "Value was empty"; + * } + * } + * @examples_end + */ + bool + parse_refresh_rate_string(const std::string &input, std::optional &output) { + static const auto is_zero { [](const auto &character) { return character == '0'; } }; + const std::string trimmed_input { boost::algorithm::trim_copy(input) }; + const std::regex refresh_rate_regex { R"(^(\d+)(?:\.(\d+))?$)" }; + + if (std::smatch match; std::regex_match(trimmed_input, match, refresh_rate_regex)) { + try { + // Here we are trimming zeros from the string to possibly reduce out of bounds case + std::string trimmed_match_1 { boost::algorithm::trim_left_copy_if(match[1].str(), is_zero) }; + if (trimmed_match_1.empty()) { + trimmed_match_1 = "0"s; // Just in case ALL the string is full of zeros, we want to leave one + } + + std::string trimmed_match_2; + if (match[2].matched) { + trimmed_match_2 = boost::algorithm::trim_right_copy_if(match[2].str(), is_zero); + } + + if (!trimmed_match_2.empty()) { + // We have a decimal point and will have to split it into numerator and denominator. + // For example: + // 59.995: + // numerator = 59995 + // denominator = 1000 + + // We are essentially removing the decimal point here: 59.995 -> 59995 + const std::string numerator_str { trimmed_match_1 + trimmed_match_2 }; + const auto numerator { stou(numerator_str) }; + + // Here we are counting decimal places and calculating denominator: 10^decimal_places + const auto denominator { static_cast(std::pow(10, trimmed_match_2.size())) }; + + output = Rational { numerator, denominator }; + } + else { + // We do not have a decimal point, just a valid number. + // For example: + // 60: + // numerator = 60 + // denominator = 1 + output = Rational { stou(trimmed_match_1), 1 }; + } + return true; + } + catch (const std::out_of_range &) { + BOOST_LOG(error) << "Failed to parse refresh rate string " << trimmed_input << " (number out of range)."; + } + catch (const std::exception &err) { + BOOST_LOG(error) << "Failed to parse refresh rate string " << trimmed_input << ":\n" + << err.what(); + } + } + else { + if (trimmed_input.empty()) { + output = std::nullopt; + return true; + } + + BOOST_LOG(error) << "Failed to parse refresh rate string " << trimmed_input << R"(. Must have a pattern of "123" or "123.456"!)"; + } + + return false; + } + + /** + * @brief Parse device preparation option from the user configuration and the session information. + * @param video_config User's video related configuration. + * @returns Parsed device preparation value we need to use. + * Empty optional if no preparation nor configuration shall take place. + * + * @examples + * const config::video_t &video_config { config::video }; + * const auto device_prep_option = parse_device_prep_option(video_config); + * @examples_end + */ + std::optional + parse_device_prep_option(const config::video_t &video_config) { + using enum config::video_t::dd_t::config_option_e; + using enum SingleDisplayConfiguration::DevicePreparation; + + switch (video_config.dd.configuration_option) { + case verify_only: + return VerifyOnly; + case ensure_active: + return EnsureActive; + case ensure_primary: + return EnsurePrimary; + case ensure_only_display: + return EnsureOnlyDisplay; + case disabled: + break; + } + + return std::nullopt; + } + + /** + * @brief Parse resolution option from the user configuration and the session information. + * @param video_config User's video related configuration. + * @param session Session information. + * @param config A reference to a display config object that will be modified on success. + * @returns True on successful parsing, false otherwise. + * + * @examples + * const std::shared_ptr launch_session; + * const config::video_t &video_config { config::video }; + * + * SingleDisplayConfiguration config; + * const bool success = parse_resolution_option(video_config, *launch_session, config); + * @examples_end + */ + bool + parse_resolution_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) { + using resolution_option_e = config::video_t::dd_t::resolution_option_e; + + switch (video_config.dd.resolution_option) { + case resolution_option_e::automatic: { + if (!session.enable_sops) { + BOOST_LOG(warning) << R"(Sunshine is configured to change resolution automatically, but the "Optimize game settings" is not set in the client! Resolution will not be changed.)"; + } + else if (session.width >= 0 && session.height >= 0) { + config.m_resolution = Resolution { + static_cast(session.width), + static_cast(session.height) + }; + } + else { + BOOST_LOG(error) << "Resolution provided by client session config is invalid: " << session.width << "x" << session.height; + return false; + } + break; + } + case resolution_option_e::manual: { + if (!session.enable_sops) { + BOOST_LOG(warning) << R"(Sunshine is configured to change resolution manually, but the "Optimize game settings" is not set in the client! Resolution will not be changed.)"; + } + else { + if (!parse_resolution_string(video_config.dd.manual_resolution, config.m_resolution)) { + BOOST_LOG(error) << "Failed to parse manual resolution string!"; + return false; + } + + if (!config.m_resolution) { + BOOST_LOG(error) << "Manual resolution must be specified!"; + return false; + } + } + break; + } + case resolution_option_e::disabled: + break; + } + + return true; + } + + /** + * @brief Parse refresh rate option from the user configuration and the session information. + * @param video_config User's video related configuration. + * @param session Session information. + * @param config A reference to a config object that will be modified on success. + * @returns True on successful parsing, false otherwise. + * + * @examples + * const std::shared_ptr launch_session; + * const config::video_t &video_config { config::video }; + * + * SingleDisplayConfiguration config; + * const bool success = parse_refresh_rate_option(video_config, *launch_session, config); + * @examples_end + */ + bool + parse_refresh_rate_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) { + using refresh_rate_option_e = config::video_t::dd_t::refresh_rate_option_e; + + switch (video_config.dd.refresh_rate_option) { + case refresh_rate_option_e::automatic: { + if (session.fps >= 0) { + config.m_refresh_rate = Rational { static_cast(session.fps), 1 }; + } + else { + BOOST_LOG(error) << "FPS value provided by client session config is invalid: " << session.fps; + return false; + } + break; + } + case refresh_rate_option_e::manual: { + if (!parse_refresh_rate_string(video_config.dd.manual_refresh_rate, config.m_refresh_rate)) { + BOOST_LOG(error) << "Failed to parse manual refresh rate string!"; + return false; + } + + if (!config.m_refresh_rate) { + BOOST_LOG(error) << "Manual refresh rate must be specified!"; + return false; + } + break; + } + case refresh_rate_option_e::disabled: + break; + } + + return true; + } + + /** + * @brief Parse HDR option from the user configuration and the session information. + * @param video_config User's video related configuration. + * @param session Session information. + * @returns Parsed HDR state value we need to switch to. + * Empty optional if no action is required. + * + * @examples + * const std::shared_ptr launch_session; + * const config::video_t &video_config { config::video }; + * const auto hdr_option = parse_hdr_option(video_config, *launch_session); + * @examples_end + */ + std::optional + parse_hdr_option(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) { + using hdr_option_e = config::video_t::dd_t::hdr_option_e; + + switch (video_config.dd.hdr_option) { + case hdr_option_e::automatic: + return session.enable_hdr ? HdrState::Enabled : HdrState::Disabled; + case hdr_option_e::disabled: + break; + } + + return std::nullopt; + } /** * @brief Construct a settings manager interface to manage display device settings. + * @param persistence_filepath File location for saving persistent state. + * @param video_config User's video related configuration. * @return An interface or nullptr if the OS does not support the interface. */ std::unique_ptr - make_settings_manager() { + make_settings_manager([[maybe_unused]] const std::filesystem::path &persistence_filepath, [[maybe_unused]] const config::video_t &video_config) { #ifdef _WIN32 - // TODO: In the upcoming PR, add audio context capture and settings persistence return std::make_unique( std::make_shared(std::make_shared()), - nullptr, - std::make_unique(nullptr), - WinWorkarounds {}); + std::make_shared(), + std::make_unique( + std::make_shared(persistence_filepath)), + WinWorkarounds { + .m_hdr_blank_delay = video_config.dd.wa.hdr_toggle ? std::make_optional(500ms) : std::nullopt }); #else return nullptr; #endif } + + /** + * @brief Defines the "revert config" algorithms. + */ + enum class revert_option_e { + try_once, ///< Try reverting once and then abort. + try_indefinitely, ///< Keep trying to revert indefinitely. + try_indefinitely_with_delay ///< Keep trying to revert indefinitely, but delay the first try by some amount of time. + }; + + /** + * @brief Reverts the configuration based on the provided option. + * @note This is function does not lock mutex. + */ + void + revert_configuration_unlocked(const revert_option_e option) { + if (!DD_DATA.sm_instance) { + // Platform is not supported, nothing to do. + return; + } + + // Note: by default the executor function is immediately executed in the calling thread. With delay, we want to avoid that. + SchedulerOptions scheduler_option { .m_sleep_durations = { DEFAULT_RETRY_INTERVAL } }; + if (option == revert_option_e::try_indefinitely_with_delay && DD_DATA.config_revert_delay > std::chrono::milliseconds::zero()) { + scheduler_option.m_sleep_durations = { DD_DATA.config_revert_delay, DEFAULT_RETRY_INTERVAL }; + scheduler_option.m_execution = SchedulerOptions::Execution::ScheduledOnly; + } + + DD_DATA.sm_instance->schedule([try_once = (option == revert_option_e::try_once)](auto &settings_iface, auto &stop_token) { + // Here we want to keep retrying indefinitely until we succeed. + if (settings_iface.revertSettings() || try_once) { + stop_token.requestStop(); + } + }, + scheduler_option); + } } // namespace std::unique_ptr - init() { - // We can support re-init without any issues, however we should make sure to cleanup first! - SM_INSTANCE = nullptr; + init(const std::filesystem::path &persistence_filepath, const config::video_t &video_config) { + std::lock_guard lock { DD_DATA.mutex }; + // We can support re-init without any issues, however we should make sure to clean up first! + revert_configuration_unlocked(revert_option_e::try_once); + DD_DATA.config_revert_delay = video_config.dd.config_revert_delay; + DD_DATA.sm_instance = nullptr; - // If we fail to create settings manager, this means platform is not supported and - // we will need to provided error-free passtrough in other methods - if (auto settings_manager { make_settings_manager() }) { - SM_INSTANCE = std::make_unique>(std::move(settings_manager)); + // If we fail to create settings manager, this means platform is not supported, and + // we will need to provided error-free pass-trough in other methods + if (auto settings_manager { make_settings_manager(persistence_filepath, video_config) }) { + DD_DATA.sm_instance = std::make_unique>(std::move(settings_manager)); - const auto available_devices { SM_INSTANCE->execute([](auto &settings_iface) { return settings_iface.enumAvailableDevices(); }) }; + const auto available_devices { DD_DATA.sm_instance->execute([](auto &settings_iface) { return settings_iface.enumAvailableDevices(); }) }; BOOST_LOG(info) << "Currently available display devices:\n" << toJson(available_devices); - // TODO: In the upcoming PR, schedule recovery here + // In case we have failed to revert configuration before shutting down, we should + // do it now. + revert_configuration_unlocked(revert_option_e::try_indefinitely); } class deinit_t: public platf::deinit_t { public: ~deinit_t() override { - // TODO: In the upcoming PR, execute recovery once here - SM_INSTANCE = nullptr; + std::lock_guard lock { DD_DATA.mutex }; + try { + // This may throw if used incorrectly. At the moment this will not happen, however + // in case some unforeseen changes are made that could raise an exception, + // we definitely don't want this to happen in destructor. Especially in the + // deinit_t where the outcome does not really matter. + revert_configuration_unlocked(revert_option_e::try_once); + } + catch (std::exception &err) { + BOOST_LOG(fatal) << err.what(); + } + + DD_DATA.sm_instance = nullptr; } }; return std::make_unique(); @@ -75,11 +538,94 @@ namespace display_device { std::string map_output_name(const std::string &output_name) { - if (!SM_INSTANCE) { + std::lock_guard lock { DD_DATA.mutex }; + if (!DD_DATA.sm_instance) { // Fallback to giving back the output name if the platform is not supported. return output_name; } - return SM_INSTANCE->execute([&output_name](auto &settings_iface) { return settings_iface.getDisplayName(output_name); }); + return DD_DATA.sm_instance->execute([&output_name](auto &settings_iface) { return settings_iface.getDisplayName(output_name); }); + } + + void + configure_display(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) { + const auto result { parse_configuration(video_config, session) }; + if (const auto *parsed_config { std::get_if(&result) }; parsed_config) { + configure_display(*parsed_config); + return; + } + + if (const auto *disabled { std::get_if(&result) }; disabled) { + revert_configuration(); + return; + } + + // Error already logged for failed_to_parse_tag_t case, and we also don't + // want to revert active configuration in case we have any + } + + void + configure_display(const SingleDisplayConfiguration &config) { + std::lock_guard lock { DD_DATA.mutex }; + if (!DD_DATA.sm_instance) { + // Platform is not supported, nothing to do. + return; + } + + DD_DATA.sm_instance->schedule([config](auto &settings_iface, auto &stop_token) { + // We only want to keep retrying in case of a transient errors. + // In other cases, when we either fail or succeed we just want to stop... + if (settings_iface.applySettings(config) != SettingsManagerInterface::ApplyResult::ApiTemporarilyUnavailable) { + stop_token.requestStop(); + } + }, + { .m_sleep_durations = { DEFAULT_RETRY_INTERVAL } }); + } + + void + revert_configuration() { + std::lock_guard lock { DD_DATA.mutex }; + revert_configuration_unlocked(revert_option_e::try_indefinitely_with_delay); + } + + bool + reset_persistence() { + std::lock_guard lock { DD_DATA.mutex }; + if (!DD_DATA.sm_instance) { + // Platform is not supported, assume success. + return true; + } + + return DD_DATA.sm_instance->execute([](auto &settings_iface, auto &stop_token) { + // Whatever the outcome is we want to stop interfering with the user, + // so any schedulers need to be stopped. + stop_token.requestStop(); + return settings_iface.resetPersistence(); + }); + } + + std::variant + parse_configuration(const config::video_t &video_config, const rtsp_stream::launch_session_t &session) { + const auto device_prep { parse_device_prep_option(video_config) }; + if (!device_prep) { + return configuration_disabled_tag_t {}; + } + + SingleDisplayConfiguration config; + config.m_device_id = video_config.output_name; + config.m_device_prep = *device_prep; + config.m_hdr_state = parse_hdr_option(video_config, session); + + if (!parse_resolution_option(video_config, session, config)) { + // Error already logged + return failed_to_parse_tag_t {}; + } + + if (!parse_refresh_rate_option(video_config, session, config)) { + // Error already logged + return failed_to_parse_tag_t {}; + } + + return config; } } // namespace display_device diff --git a/src/display_device.h b/src/display_device.h index 6562f5a3..e17c408f 100644 --- a/src/display_device.h +++ b/src/display_device.h @@ -5,24 +5,35 @@ #pragma once // lib includes +#include +#include #include // forward declarations namespace platf { class deinit_t; -} // namespace platf +} +namespace config { + struct video_t; +} +namespace rtsp_stream { + struct launch_session_t; +} namespace display_device { /** * @brief Initialize the implementation and perform the initial state recovery (if needed). + * @param persistence_filepath File location for reading/saving persistent state. + * @param video_config User's video related configuration. * @returns A deinit_t instance that performs cleanup when destroyed. * * @examples - * const auto init_guard { display_device::init() }; + * const config::video_t &video_config { config::video }; + * const auto init_guard { init("/my/persitence/file.state", video_config) }; * @examples_end */ - std::unique_ptr - init(); + [[nodiscard]] std::unique_ptr + init(const std::filesystem::path &persistence_filepath, const config::video_t &video_config); /** * @brief Map the output name to a specific display. @@ -34,6 +45,111 @@ namespace display_device { * const auto mapped_name_custom { map_output_name("{some-device-id}") }; * @examples_end */ - std::string + [[nodiscard]] std::string map_output_name(const std::string &output_name); + + /** + * @brief Configure the display device based on the user configuration and the session information. + * @note This is a convenience method for calling similar method of a different signature. + * + * @param video_config User's video related configuration. + * @param session Session information. + * + * @examples + * const std::shared_ptr launch_session; + * const config::video_t &video_config { config::video }; + * + * configure_display(video_config, *launch_session); + * @examples_end + */ + void + configure_display(const config::video_t &video_config, const rtsp_stream::launch_session_t &session); + + /** + * @brief Configure the display device using the provided configuration. + * + * In some cases configuring display can fail due to transient issues and + * we will keep trying every 5 seconds, even if the stream has already started as there was + * no possibility to apply settings before the stream start. + * + * Therefore, there is no return value as we still want to continue with the stream, so that + * the users can do something about it once they are connected. Otherwise, we might + * prevent users from logging in at all if we keep failing to apply configuration. + * + * @param config Configuration for the display. + * + * @examples + * const SingleDisplayConfiguration valid_config { }; + * configure_display(valid_config); + * @examples_end + */ + void + configure_display(const SingleDisplayConfiguration &config); + + /** + * @brief Revert the display configuration and restore the previous state. + * + * In case the state could not be restored, by default it will be retried again in 5 seconds + * (repeating indefinitely until success or until persistence is reset). + * + * @examples + * revert_configuration(); + * @examples_end + */ + void + revert_configuration(); + + /** + * @brief Reset the persistence and currently held initial display state. + * + * This is normally used to get out of the "broken" state where the algorithm wants + * to restore the initial display state, but it is no longer possible. + * + * This could happen if the display is no longer available or the hardware was changed + * and the device ids no longer match. + * + * The user then accepts that Sunshine is not able to restore the state and "agrees" to + * do it manually. + * + * @return + * @note Whether the function succeeds or fails, the any of the scheduled "retries" from + * other methods will be stopped to not interfere with the user actions. + * + * @examples + * const auto result = reset_persistence(); + * @examples_end + */ + [[nodiscard]] bool + reset_persistence(); + + /** + * @brief A tag structure indicating that configuration parsing has failed. + */ + struct failed_to_parse_tag_t {}; + + /** + * @brief A tag structure indicating that configuration is disabled. + */ + struct configuration_disabled_tag_t {}; + + /** + * @brief Parse the user configuration and the session information. + * @param video_config User's video related configuration. + * @param session Session information. + * @return Parsed single display configuration or + * a tag indicating that the parsing has failed or + * a tag indicating that the user does not want to perform any configuration. + * + * @examples + * const std::shared_ptr launch_session; + * const config::video_t &video_config { config::video }; + * + * const auto config { parse_configuration(video_config, *launch_session) }; + * if (const auto *parsed_config { std::get_if(&result) }; parsed_config) { + * configure_display(*config); + * } + * @examples_end + */ + [[nodiscard]] std::variant + parse_configuration(const config::video_t &video_config, const rtsp_stream::launch_session_t &session); } // namespace display_device diff --git a/src/main.cpp b/src/main.cpp index b9ffc049..04ab7d13 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -137,7 +137,7 @@ main(int argc, char *argv[]) { // Adding guard here first as it also performs recovery after crash, // otherwise people could theoretically end up without display output. // It also should be destroyed before forced shutdown to expedite the cleanup. - auto display_device_deinit_guard = display_device::init(); + auto display_device_deinit_guard = display_device::init(platf::appdata() / "display_device.state", config::video); if (!display_device_deinit_guard) { BOOST_LOG(error) << "Display device session failed to initialize"sv; } diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 2441aed9..e510d083 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -22,6 +22,7 @@ // local includes #include "config.h" #include "crypto.h" +#include "display_device.h" #include "file_handler.h" #include "globals.h" #include "httpcommon.h" @@ -812,12 +813,17 @@ namespace nvhttp { print_req(request); pt::ptree tree; + bool revert_display_configuration { false }; auto g = util::fail_guard([&]() { std::ostringstream data; pt::write_xml(data, tree); response->write(data.str()); response->close_connection_after_response = true; + + if (revert_display_configuration) { + display_device::revert_configuration(); + } }); auto args = request->parse_query_string(); @@ -844,11 +850,22 @@ namespace nvhttp { return; } - // Probe encoders again before streaming to ensure our chosen - // encoder matches the active GPU (which could have changed - // due to hotplugging, driver crash, primary monitor change, - // or any number of other factors). + host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); + auto launch_session = make_launch_session(host_audio, args); + if (rtsp_stream::session_count() == 0) { + // We want to prepare display only if there are no active sessions at + // the moment. This should be done before probing encoders as it could + // change the active displays. + display_device::configure_display(config::video, *launch_session); + + // The display should be restored in case something fails as there are no other sessions. + revert_display_configuration = true; + + // Probe encoders again before streaming to ensure our chosen + // encoder matches the active GPU (which could have changed + // due to hotplugging, driver crash, primary monitor change, + // or any number of other factors). if (video::probe_encoders()) { tree.put("root..status_code", 503); tree.put("root..status_message", "Failed to initialize video capture/encoding. Is a display connected and turned on?"); @@ -858,9 +875,6 @@ namespace nvhttp { } } - host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); - auto launch_session = make_launch_session(host_audio, args); - auto encryption_mode = net::encryption_mode_for_address(request->remote_endpoint().address()); if (!launch_session->rtsp_cipher && encryption_mode == config::ENCRYPTION_MODE_MANDATORY) { BOOST_LOG(error) << "Rejecting client that cannot comply with mandatory encryption requirement"sv; @@ -890,6 +904,9 @@ namespace nvhttp { tree.put("root.gamesession", 1); rtsp_stream::launch_session_raise(launch_session); + + // Stream was started successfully, we will revert the config when the app or session terminates + revert_display_configuration = false; } void @@ -925,7 +942,21 @@ namespace nvhttp { return; } - if (rtsp_stream::session_count() == 0) { + // Newer Moonlight clients send localAudioPlayMode on /resume too, + // so we should use it if it's present in the args and there are + // no active sessions we could be interfering with. + const bool no_active_sessions { rtsp_stream::session_count() == 0 }; + if (no_active_sessions && args.find("localAudioPlayMode"s) != std::end(args)) { + host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); + } + const auto launch_session = make_launch_session(host_audio, args); + + if (no_active_sessions) { + // We want to prepare display only if there are no active sessions at + // the moment. This should be done before probing encoders as it could + // change the active displays. + display_device::configure_display(config::video, *launch_session); + // Probe encoders again before streaming to ensure our chosen // encoder matches the active GPU (which could have changed // due to hotplugging, driver crash, primary monitor change, @@ -937,17 +968,8 @@ namespace nvhttp { return; } - - // Newer Moonlight clients send localAudioPlayMode on /resume too, - // so we should use it if it's present in the args and there are - // no active sessions we could be interfering with. - if (args.find("localAudioPlayMode"s) != std::end(args)) { - host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); - } } - auto launch_session = make_launch_session(host_audio, args); - auto encryption_mode = net::encryption_mode_for_address(request->remote_endpoint().address()); if (!launch_session->rtsp_cipher && encryption_mode == config::ENCRYPTION_MODE_MANDATORY) { BOOST_LOG(error) << "Rejecting client that cannot comply with mandatory encryption requirement"sv; @@ -989,6 +1011,9 @@ namespace nvhttp { if (proc::proc.running() > 0) { proc::proc.terminate(); } + + // The config needs to be reverted regardless of whether "proc::proc.terminate()" was called or not. + display_device::revert_configuration(); } void diff --git a/src/platform/common.h b/src/platform/common.h index 4b2ca66a..abcbefc8 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -550,6 +550,14 @@ namespace platf { virtual std::unique_ptr microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) = 0; + /** + * @brief Check if the audio sink is available in the system. + * @param sink Sink to be checked. + * @returns True if available, false otherwise. + */ + virtual bool + is_sink_available(const std::string &sink) = 0; + virtual std::optional sink_info() = 0; diff --git a/src/platform/linux/audio.cpp b/src/platform/linux/audio.cpp index ff231707..a48ee2f0 100644 --- a/src/platform/linux/audio.cpp +++ b/src/platform/linux/audio.cpp @@ -473,6 +473,12 @@ namespace platf { return ::platf::microphone(mapping, channels, sample_rate, frame_size, get_monitor_name(sink_name)); } + bool + is_sink_available(const std::string &sink) override { + BOOST_LOG(warning) << "audio_control_t::is_sink_available() unimplemented: "sv << sink; + return true; + } + int set_sink(const std::string &sink) override { auto alarm = safe::make_alarm(); diff --git a/src/platform/macos/microphone.mm b/src/platform/macos/microphone.mm index 1e3a4cd6..8d2129f2 100644 --- a/src/platform/macos/microphone.mm +++ b/src/platform/macos/microphone.mm @@ -81,6 +81,12 @@ namespace platf { return mic; } + bool + is_sink_available(const std::string &sink) override { + BOOST_LOG(warning) << "audio_control_t::is_sink_available() unimplemented: "sv << sink; + return true; + } + std::optional sink_info() override { sink_t sink; diff --git a/src/platform/windows/audio.cpp b/src/platform/windows/audio.cpp index 3335eeb0..3c401976 100644 --- a/src/platform/windows/audio.cpp +++ b/src/platform/windows/audio.cpp @@ -722,6 +722,13 @@ namespace platf::audio { return sink; } + bool + is_sink_available(const std::string &sink) override { + const auto match_list = match_all_fields(from_utf8(sink)); + const auto matched = find_device_id(match_list); + return static_cast(matched); + } + /** * @brief Extract virtual audio sink information possibly encoded in the sink name. * @param sink The sink name diff --git a/src/process.cpp b/src/process.cpp index 1c78ff4d..3ee9d6b9 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -23,6 +23,7 @@ #include "config.h" #include "crypto.h" +#include "display_device.h" #include "logging.h" #include "platform/common.h" #include "system_tray.h" @@ -341,16 +342,19 @@ namespace proc { } _pipe.reset(); -#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1 + bool has_run = _app_id > 0; // Only show the Stopped notification if we actually have an app to stop // Since terminate() is always run when a new app has started if (proc::proc.get_last_run_app_name().length() > 0 && has_run) { +#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1 system_tray::update_tray_stopped(proc::proc.get_last_run_app_name()); - } #endif + display_device::revert_configuration(); + } + _app_id = -1; } diff --git a/src/stream.cpp b/src/stream.cpp index 8fa09bec..040a69e9 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -20,6 +20,7 @@ extern "C" { } #include "config.h" +#include "display_device.h" #include "globals.h" #include "input.h" #include "logging.h" @@ -1948,11 +1949,15 @@ namespace stream { // If this is the last session, invoke the platform callbacks if (--running_sessions == 0) { -#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1 if (proc::proc.running()) { +#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1 system_tray::update_tray_pausing(proc::proc.get_last_run_app_name()); - } #endif + } + else { + display_device::revert_configuration(); + } + platf::streaming_will_stop(); } diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index a7681fdd..300e1c36 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -170,6 +170,14 @@ "install_steam_audio_drivers": "enabled", "adapter_name": "", "output_name": "", + "dd_configuration_option": "verify_only", + "dd_resolution_option": "auto", + "dd_manual_resolution": "", + "dd_refresh_rate_option": "auto", + "dd_manual_refresh_rate": "", + "dd_hdr_option": "auto", + "dd_config_revert_delay": 3000, + "dd_wa_hdr_toggle": "disabled", "min_fps_factor": 1, }, }, diff --git a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue index 430fe1fa..531d0972 100644 --- a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue +++ b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue @@ -74,6 +74,11 @@ const config = ref(props.config) :config="config" /> + + import { ref } from 'vue' -import { $tp } from '../../../platform-i18n' import PlatformLayout from '../../../PlatformLayout.vue' +import Checkbox from "../../../Checkbox.vue"; const props = defineProps({ platform: String, - config: Object, - display_mode_remapping: Array + config: Object }) const config = ref(props.config) -const display_mode_remapping = ref(props.display_mode_remapping) - -// TODO: Sample for use in PR #2032 -function getRemappingType() -{ - // Assuming here that at least one setting is set to "automatic" - if (config.value.resolution_change !== 'automatic') { - return "refresh_rate_only"; - } - if (config.value.refresh_rate_change !== 'automatic') { - return "resolution_only"; - } - return ""; -} - -function addRemapping(type) { - let template = { - type: type, - received_resolution: "", - received_fps: "", - final_resolution: "", - final_refresh_rate: "", - }; - - display_mode_remapping.value.push(template); -} diff --git a/src_assets/common/assets/web/public/assets/locale/en.json b/src_assets/common/assets/web/public/assets/locale/en.json index d3d9356f..968bb517 100644 --- a/src_assets/common/assets/web/public/assets/locale/en.json +++ b/src_assets/common/assets/web/public/assets/locale/en.json @@ -152,6 +152,30 @@ "controller_desc": "Allows guests to control the host system with a gamepad / controller", "credentials_file": "Credentials File", "credentials_file_desc": "Store Username/Password separately from Sunshine's state file.", + "dd_config_ensure_active": "Activate the display automatically", + "dd_config_ensure_only_display": "Deactivate other displays and activate only the specified display", + "dd_config_ensure_primary": "Activate the display automatically and make it a primary display", + "dd_config_label": "Device configuration", + "dd_config_revert_delay": "Config revert delay", + "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_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", + "dd_options_header": "Advanced display device options", + "dd_refresh_rate_option": "Refresh rate", + "dd_refresh_rate_option_auto": "Use FPS value provided by the client (default)", + "dd_refresh_rate_option_disabled": "Do not change refresh rate", + "dd_refresh_rate_option_manual": "Use manually entered refresh rate", + "dd_refresh_rate_option_manual_desc": "Enter the refresh rate to be used", + "dd_resolution_option": "Resolution", + "dd_resolution_option_auto": "Use resolution provided by the client (default)", + "dd_resolution_option_disabled": "Do not change resolution", + "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_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", @@ -379,6 +403,10 @@ "third_party_notice": "Third Party Notice" }, "troubleshooting": { + "dd_reset": "Reset Persistent Display Device Settings", + "dd_reset_desc": "If Sunshine is stuck trying to restore the changed display device settings, you can reset the settings and proceed to restore the display state manually.", + "dd_reset_error": "Error while resetting persistence!", + "dd_reset_success": "Success resetting persistence!", "force_close": "Force Close", "force_close_desc": "If Moonlight complains about an app currently running, force closing the app should fix the issue.", "force_close_error": "Error while closing Application", diff --git a/src_assets/common/assets/web/troubleshooting.html b/src_assets/common/assets/web/troubleshooting.html index 5e02b8c8..9ac45698 100644 --- a/src_assets/common/assets/web/troubleshooting.html +++ b/src_assets/common/assets/web/troubleshooting.html @@ -75,6 +75,25 @@ + +
+
+

{{ $t('troubleshooting.dd_reset') }}

+
+

{{ $t('troubleshooting.dd_reset_desc') }}

+
+ {{ $t('troubleshooting.dd_reset_success') }} +
+
+ {{ $t('troubleshooting.dd_reset_error') }} +
+
+ +
+
+
@@ -141,11 +160,14 @@ clients: [], closeAppPressed: false, closeAppStatus: null, + ddResetPressed: false, + ddResetStatus: null, logs: 'Loading...', logFilter: null, logInterval: null, restartPressed: false, showApplyMessage: false, + platform: "", unpairAllPressed: false, unpairAllStatus: null, }; @@ -159,6 +181,12 @@ } }, created() { + fetch("/api/config") + .then((r) => r.json()) + .then((r) => { + this.platform = r.platform; + }); + this.logInterval = setInterval(() => { this.refreshLogs(); }, 5000); @@ -236,6 +264,18 @@ method: "POST", }); }, + ddResetPersistence() { + this.ddResetPressed = true; + fetch("/api/reset-display-device-persistence", { method: "POST" }) + .then((r) => r.json()) + .then((r) => { + this.ddResetPressed = false; + this.ddResetStatus = r.status.toString() === "true"; + setTimeout(() => { + this.ddResetStatus = null; + }, 5000); + }); + }, }, }); diff --git a/tests/unit/test_display_device.cpp b/tests/unit/test_display_device.cpp new file mode 100644 index 00000000..f08a5d62 --- /dev/null +++ b/tests/unit/test_display_device.cpp @@ -0,0 +1,276 @@ +/** + * @file tests/unit/test_display_device.cpp + * @brief Test src/display_device.*. + */ +#include "../tests_common.h" + +#include +#include +#include + +namespace { + using config_option_e = config::video_t::dd_t::config_option_e; + using device_prep_t = display_device::SingleDisplayConfiguration::DevicePreparation; + + using hdr_option_e = config::video_t::dd_t::hdr_option_e; + using hdr_state_e = display_device::HdrState; + + using resolution_option_e = config::video_t::dd_t::resolution_option_e; + using resolution_t = display_device::Resolution; + + using refresh_rate_option_e = config::video_t::dd_t::refresh_rate_option_e; + using rational_t = display_device::Rational; + + struct failed_to_parse_resolution_tag_t {}; + struct failed_to_parse_refresh_rate_tag_t {}; + struct no_refresh_rate_tag_t {}; + struct no_resolution_tag_t {}; + + struct client_resolution_t { + int width; + int height; + }; + + using client_fps_t = int; + using sops_enabled_t = bool; + using client_wants_hdr_t = bool; + + constexpr unsigned int max_uint { std::numeric_limits::max() }; + const std::string max_uint_string { std::to_string(std::numeric_limits::max()) }; + + template + struct DisplayDeviceConfigTest: testing::TestWithParam {}; +} // namespace + +using ParseDeviceId = DisplayDeviceConfigTest>; +INSTANTIATE_TEST_SUITE_P( + DisplayDeviceConfigTest, + ParseDeviceId, + testing::Values( + std::make_pair(""s, ""s), + std::make_pair("SomeId"s, "SomeId"s), + std::make_pair("{daeac860-f4db-5208-b1f5-cf59444fb768}"s, "{daeac860-f4db-5208-b1f5-cf59444fb768}"s))); +TEST_P(ParseDeviceId, IntegrationTest) { + const auto &[input_value, expected_value] = GetParam(); + + config::video_t video_config {}; + video_config.dd.configuration_option = config_option_e::verify_only; + video_config.output_name = input_value; + + const auto result { display_device::parse_configuration(video_config, {}) }; + EXPECT_EQ(std::get(result).m_device_id, expected_value); +} + +using ParseConfigOption = DisplayDeviceConfigTest>>; +INSTANTIATE_TEST_SUITE_P( + DisplayDeviceConfigTest, + ParseConfigOption, + testing::Values( + std::make_pair(config_option_e::disabled, std::nullopt), + std::make_pair(config_option_e::verify_only, device_prep_t::VerifyOnly), + std::make_pair(config_option_e::ensure_active, device_prep_t::EnsureActive), + std::make_pair(config_option_e::ensure_primary, device_prep_t::EnsurePrimary), + std::make_pair(config_option_e::ensure_only_display, device_prep_t::EnsureOnlyDisplay))); +TEST_P(ParseConfigOption, IntegrationTest) { + const auto &[input_value, expected_value] = GetParam(); + + config::video_t video_config {}; + video_config.dd.configuration_option = input_value; + + const auto result { display_device::parse_configuration(video_config, {}) }; + if (const auto *parsed_config { std::get_if(&result) }; parsed_config) { + ASSERT_EQ(parsed_config->m_device_prep, expected_value); + } + else { + ASSERT_EQ(std::get_if(&result) != nullptr, !expected_value); + } +} + +using ParseHdrOption = DisplayDeviceConfigTest, std::optional>>; +INSTANTIATE_TEST_SUITE_P( + DisplayDeviceConfigTest, + ParseHdrOption, + testing::Values( + std::make_pair(std::make_pair(hdr_option_e::disabled, client_wants_hdr_t { true }), std::nullopt), + std::make_pair(std::make_pair(hdr_option_e::disabled, client_wants_hdr_t { false }), std::nullopt), + std::make_pair(std::make_pair(hdr_option_e::automatic, client_wants_hdr_t { true }), hdr_state_e::Enabled), + std::make_pair(std::make_pair(hdr_option_e::automatic, client_wants_hdr_t { false }), hdr_state_e::Disabled))); +TEST_P(ParseHdrOption, IntegrationTest) { + const auto &[input_value, expected_value] = GetParam(); + const auto &[input_hdr_option, input_enable_hdr] = input_value; + + config::video_t video_config {}; + video_config.dd.configuration_option = config_option_e::verify_only; + video_config.dd.hdr_option = input_hdr_option; + + rtsp_stream::launch_session_t session {}; + session.enable_hdr = input_enable_hdr; + + const auto result { display_device::parse_configuration(video_config, session) }; + EXPECT_EQ(std::get(result).m_hdr_state, expected_value); +} + +using ParseResolutionOption = DisplayDeviceConfigTest>, + std::variant>>; +INSTANTIATE_TEST_SUITE_P( + DisplayDeviceConfigTest, + ParseResolutionOption, + testing::Values( + //---- Disabled cases ---- + std::make_pair(std::make_tuple(resolution_option_e::disabled, sops_enabled_t { true }, client_resolution_t { 1920, 1080 }), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::disabled, sops_enabled_t { true }, "1920x1080"s), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::disabled, sops_enabled_t { true }, client_resolution_t { -1, -1 }), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::disabled, sops_enabled_t { true }, "invalid_res"s), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::disabled, sops_enabled_t { false }, client_resolution_t { 1920, 1080 }), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::disabled, sops_enabled_t { false }, "1920x1080"s), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::disabled, sops_enabled_t { false }, client_resolution_t { -1, -1 }), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::disabled, sops_enabled_t { false }, "invalid_res"s), no_resolution_tag_t {}), + //---- Automatic cases ---- + std::make_pair(std::make_tuple(resolution_option_e::automatic, sops_enabled_t { true }, client_resolution_t { 1920, 1080 }), resolution_t { 1920, 1080 }), + std::make_pair(std::make_tuple(resolution_option_e::automatic, sops_enabled_t { true }, "1920x1080"s), resolution_t {}), + std::make_pair(std::make_tuple(resolution_option_e::automatic, sops_enabled_t { true }, client_resolution_t { -1, -1 }), failed_to_parse_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::automatic, sops_enabled_t { true }, "invalid_res"s), resolution_t {}), + std::make_pair(std::make_tuple(resolution_option_e::automatic, sops_enabled_t { false }, client_resolution_t { 1920, 1080 }), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::automatic, sops_enabled_t { false }, "1920x1080"s), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::automatic, sops_enabled_t { false }, client_resolution_t { -1, -1 }), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::automatic, sops_enabled_t { false }, "invalid_res"s), no_resolution_tag_t {}), + //---- Manual cases ---- + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, client_resolution_t { 1920, 1080 }), failed_to_parse_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, "1920x1080"s), resolution_t { 1920, 1080 }), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, client_resolution_t { -1, -1 }), failed_to_parse_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, "invalid_res"s), failed_to_parse_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { false }, client_resolution_t { 1920, 1080 }), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { false }, "1920x1080"s), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { false }, client_resolution_t { -1, -1 }), no_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { false }, "invalid_res"s), no_resolution_tag_t {}), + //---- Both negative values from client are checked ---- + std::make_pair(std::make_tuple(resolution_option_e::automatic, sops_enabled_t { true }, client_resolution_t { 0, 0 }), resolution_t { 0, 0 }), + std::make_pair(std::make_tuple(resolution_option_e::automatic, sops_enabled_t { true }, client_resolution_t { -1, 0 }), failed_to_parse_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::automatic, sops_enabled_t { true }, client_resolution_t { 0, -1 }), failed_to_parse_resolution_tag_t {}), + //---- Resolution string format validation ---- + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, "0x0"s), resolution_t { 0, 0 }), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, "0x"s), failed_to_parse_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, "x0"s), failed_to_parse_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, "-1x1"s), failed_to_parse_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, "1x-1"s), failed_to_parse_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, "x0x0"s), failed_to_parse_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, "0x0x"s), failed_to_parse_resolution_tag_t {}), + //---- String number is out of bounds ---- + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, max_uint_string + "x"s + max_uint_string), resolution_t { max_uint, max_uint }), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, max_uint_string + "0"s + "x"s + max_uint_string), failed_to_parse_resolution_tag_t {}), + std::make_pair(std::make_tuple(resolution_option_e::manual, sops_enabled_t { true }, max_uint_string + "x"s + max_uint_string + "0"s), failed_to_parse_resolution_tag_t {}))); +TEST_P(ParseResolutionOption, IntegrationTest) { + const auto &[input_value, expected_value] = GetParam(); + const auto &[input_resolution_option, input_enable_sops, input_resolution] = input_value; + + config::video_t video_config {}; + video_config.dd.configuration_option = config_option_e::verify_only; + video_config.dd.resolution_option = input_resolution_option; + + rtsp_stream::launch_session_t session {}; + session.enable_sops = input_enable_sops; + + if (const auto *client_res { std::get_if(&input_resolution) }; client_res) { + video_config.dd.manual_resolution = {}; + session.width = client_res->width; + session.height = client_res->height; + } + else { + video_config.dd.manual_resolution = std::get(input_resolution); + session.width = {}; + session.height = {}; + } + + const auto result { display_device::parse_configuration(video_config, session) }; + if (const auto *failed_option { std::get_if(&expected_value) }; failed_option) { + EXPECT_NO_THROW(std::get(result)); + } + else { + std::optional expected_resolution; + if (const auto *valid_resolution_option { std::get_if(&expected_value) }; valid_resolution_option) { + expected_resolution = *valid_resolution_option; + } + + EXPECT_EQ(std::get(result).m_resolution, expected_resolution); + } +} + +using ParseRefreshRateOption = DisplayDeviceConfigTest>, + std::variant>>; +INSTANTIATE_TEST_SUITE_P( + DisplayDeviceConfigTest, + ParseRefreshRateOption, + testing::Values( + //---- Disabled cases ---- + std::make_pair(std::make_tuple(refresh_rate_option_e::disabled, client_fps_t { 60 }), no_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::disabled, "60"s), no_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::disabled, "59.9885"s), no_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::disabled, client_fps_t { -1 }), no_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::disabled, "invalid_refresh_rate"s), no_refresh_rate_tag_t {}), + //---- Automatic cases ---- + std::make_pair(std::make_tuple(refresh_rate_option_e::automatic, client_fps_t { 60 }), rational_t { 60, 1 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::automatic, "60"s), rational_t { 0, 1 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::automatic, "59.9885"s), rational_t { 0, 1 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::automatic, client_fps_t { -1 }), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::automatic, "invalid_refresh_rate"s), rational_t { 0, 1 }), + //---- Manual cases ---- + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, client_fps_t { 60 }), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "60"s), rational_t { 60, 1 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "59.9885"s), rational_t { 599885, 10000 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, client_fps_t { -1 }), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "invalid_refresh_rate"s), failed_to_parse_refresh_rate_tag_t {}), + //---- Refresh rate string format validation ---- + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "0000000000000"s), rational_t { 0, 1 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "0"s), rational_t { 0, 1 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "00000000.0000000"s), rational_t { 0, 1 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "0.0"s), rational_t { 0, 1 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "000000000000010"s), rational_t { 10, 1 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "00000010.0000000"s), rational_t { 10, 1 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "00000010.1000000"s), rational_t { 101, 10 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "00000010.0100000"s), rational_t { 1001, 100 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "00000000.1000000"s), rational_t { 1, 10 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "60,0"s), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "-60.0"s), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "60.-0"s), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "a60.0"s), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "60.0b"s), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "a60"s), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "60b"s), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, "-60"s), failed_to_parse_refresh_rate_tag_t {}), + //---- String number is out of bounds ---- + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, max_uint_string), rational_t { max_uint, 1 }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, max_uint_string + "0"s), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, max_uint_string.substr(0, 1) + "."s + max_uint_string.substr(1)), rational_t { max_uint, static_cast(std::pow(10, max_uint_string.size() - 1)) }), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, max_uint_string.substr(0, 1) + "0"s + "."s + max_uint_string.substr(1)), failed_to_parse_refresh_rate_tag_t {}), + std::make_pair(std::make_tuple(refresh_rate_option_e::manual, max_uint_string.substr(0, 1) + "."s + "0"s + max_uint_string.substr(1)), failed_to_parse_refresh_rate_tag_t {}))); +TEST_P(ParseRefreshRateOption, IntegrationTest) { + const auto &[input_value, expected_value] = GetParam(); + const auto &[input_refresh_rate_option, input_refresh_rate] = input_value; + + config::video_t video_config {}; + video_config.dd.configuration_option = config_option_e::verify_only; + video_config.dd.refresh_rate_option = input_refresh_rate_option; + + rtsp_stream::launch_session_t session {}; + if (const auto *client_refresh_rate { std::get_if(&input_refresh_rate) }; client_refresh_rate) { + video_config.dd.manual_refresh_rate = {}; + session.fps = *client_refresh_rate; + } + else { + video_config.dd.manual_refresh_rate = std::get(input_refresh_rate); + session.fps = {}; + } + + const auto result { display_device::parse_configuration(video_config, session) }; + if (const auto *failed_option { std::get_if(&expected_value) }; failed_option) { + EXPECT_NO_THROW(std::get(result)); + } + else { + std::optional expected_refresh_rate; + if (const auto *valid_refresh_rate_option { std::get_if(&expected_value) }; valid_refresh_rate_option) { + expected_refresh_rate = *valid_refresh_rate_option; + } + + EXPECT_EQ(std::get(result).m_refresh_rate, expected_refresh_rate); + } +} From 9b9767be0c7f8f169029c3fa1f5ce92dd5ea416c Mon Sep 17 00:00:00 2001 From: LizardByte-bot <108553330+LizardByte-bot@users.noreply.github.com> Date: Tue, 7 Jan 2025 22:33:07 -0500 Subject: [PATCH 10/20] chore(l10n): update translations (#3512) Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> --- docs/configuration.md | 6 +- scripts/_locale.py | 6 + src/config.cpp | 1 + .../assets/web/configs/tabs/General.vue | 1 + .../assets/web/public/assets/locale/bg.json | 33 +- .../assets/web/public/assets/locale/de.json | 33 +- .../web/public/assets/locale/en_GB.json | 33 +- .../web/public/assets/locale/en_US.json | 33 +- .../assets/web/public/assets/locale/es.json | 39 +- .../assets/web/public/assets/locale/fr.json | 33 +- .../assets/web/public/assets/locale/it.json | 33 +- .../assets/web/public/assets/locale/ja.json | 33 +- .../assets/web/public/assets/locale/ko.json | 438 ++++++++++++++++++ .../assets/web/public/assets/locale/pl.json | 33 +- .../assets/web/public/assets/locale/pt.json | 33 +- .../web/public/assets/locale/pt_BR.json | 33 +- .../assets/web/public/assets/locale/ru.json | 33 +- .../assets/web/public/assets/locale/sv.json | 33 +- .../assets/web/public/assets/locale/tr.json | 33 +- .../assets/web/public/assets/locale/uk.json | 33 +- .../assets/web/public/assets/locale/zh.json | 47 +- 21 files changed, 973 insertions(+), 27 deletions(-) create mode 100644 src_assets/common/assets/web/public/assets/locale/ko.json diff --git a/docs/configuration.md b/docs/configuration.md index 139b7501..78dacddd 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -57,7 +57,7 @@ editing the `conf` file in a text editor. Use the examples as reference. @endcode
- + @@ -93,6 +93,10 @@ editing the `conf` file in a text editor. Use the examples as reference. + + + + diff --git a/scripts/_locale.py b/scripts/_locale.py index d0354149..84ae3385 100644 --- a/scripts/_locale.py +++ b/scripts/_locale.py @@ -24,6 +24,7 @@ year = datetime.datetime.now().year # target locales target_locales = [ + 'bg', # Bulgarian 'de', # German 'en', # English 'en_GB', # English (United Kingdom) @@ -32,9 +33,14 @@ target_locales = [ 'fr', # French 'it', # Italian 'ja', # Japanese + 'ko', # Korean + 'pl', # Polish 'pt', # Portuguese + 'pt_BR', # Portuguese (Brazil) 'ru', # Russian 'sv', # Swedish + 'tr', # Turkish + 'uk', # Ukrainian 'zh', # Chinese ] diff --git a/src/config.cpp b/src/config.cpp index 43527f45..cb3337d5 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1208,6 +1208,7 @@ namespace config { "fr"sv, // French "it"sv, // Italian "ja"sv, // Japanese + "ko"sv, // Korean "pl"sv, // Polish "pt"sv, // Portuguese "pt_BR"sv, // Portuguese (Brazilian) diff --git a/src_assets/common/assets/web/configs/tabs/General.vue b/src_assets/common/assets/web/configs/tabs/General.vue index d08bec55..74f43938 100644 --- a/src_assets/common/assets/web/configs/tabs/General.vue +++ b/src_assets/common/assets/web/configs/tabs/General.vue @@ -43,6 +43,7 @@ function removeCmd(index) { + diff --git a/src_assets/common/assets/web/public/assets/locale/bg.json b/src_assets/common/assets/web/public/assets/locale/bg.json index 4ac627e6..5e7610b3 100644 --- a/src_assets/common/assets/web/public/assets/locale/bg.json +++ b/src_assets/common/assets/web/public/assets/locale/bg.json @@ -7,11 +7,13 @@ "cancel": "Отказ", "disabled": "Изключено", "disabled_def": "Изключено (по подразбиране)", + "disabled_def_cbox": "По подразбиране: отметнато", "dismiss": "Отхвърляне", "do_cmd": "Команда преди стартиране", "elevated": "Изпълнение като администратор", "enabled": "Включено", "enabled_def": "Включено (по подразбиране)", + "enabled_def_cbox": "По подразбиране: проверено", "error": "Грешка!", "note": "Забележка:", "password": "Парола", @@ -150,6 +152,30 @@ "controller_desc": "Позволява на клиентите да управляват отдалечения компютър с контролер", "credentials_file": "Файл с удостоверителни данни", "credentials_file_desc": "Съхраняване на потребителското име/парола отделно от файла за състоянието на Sunshine.", + "dd_config_ensure_active": "Автоматично активиране на дисплея", + "dd_config_ensure_only_display": "Деактивиране на други дисплеи и активиране само на посочения дисплей", + "dd_config_ensure_primary": "Автоматично активиране на дисплея и превръщането му в основен дисплей", + "dd_config_label": "Конфигурация на устройството", + "dd_config_revert_delay": "Забавяне на връщането на конфигурацията", + "dd_config_revert_delay_desc": "Допълнително забавяне в милисекунди, което да се изчака, преди да се върне конфигурацията, когато приложението е затворено или последната сесия е прекратена. Основната цел е да се осигури по-плавен преход при бързо превключване между приложенията.", + "dd_config_verify_only": "Проверете дали дисплеят е включен (по подразбиране)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "Включване/изключване на режима HDR по заявка на клиента (по подразбиране)", + "dd_hdr_option_disabled": "Не променяйте настройките на HDR", + "dd_options_header": "Разширени опции на устройството за показване", + "dd_refresh_rate_option": "Честота на опресняване", + "dd_refresh_rate_option_auto": "Използвайте стойността на FPS, предоставена от клиента (по подразбиране)", + "dd_refresh_rate_option_disabled": "Не променяйте честотата на опресняване", + "dd_refresh_rate_option_manual": "Използване на ръчно въведена честота на опресняване", + "dd_refresh_rate_option_manual_desc": "Въведете честотата на опресняване, която ще се използва", + "dd_resolution_option": "Резолюция", + "dd_resolution_option_auto": "Използвайте резолюция, предоставена от клиента (по подразбиране)", + "dd_resolution_option_disabled": "Не променяйте резолюцията", + "dd_resolution_option_manual": "Използване на ръчно въведена резолюция", + "dd_resolution_option_manual_desc": "Въведете разделителната способност, която ще се използва", + "dd_resolution_option_ogs_desc": "Опцията \"Оптимизиране на настройките на играта\" трябва да бъде активирана в клиента на Moonlight, за да работи това.", + "dd_wa_hdr_toggle_desc": "Когато използвате виртуално дисплейно устройство за стрийминг, то може да покаже неправилен HDR цвят. С активирането на тази опция Sunshine ще се опита да смекчи този проблем.", + "dd_wa_hdr_toggle": "Активиране на заобикаляне на висококонтрастния режим за HDR", "ds4_back_as_touchpad_click": "Симулиране на бутона Back/Select чрез натискане на сензорния панел", "ds4_back_as_touchpad_click_desc": "При принудително симулиране на контролер DualShock 4, да се симулира натискането на бутона Back/Select чрез натискане на сензорния панел", "encoder": "Принудително използване на определен метод на кодиране", @@ -168,6 +194,7 @@ "gamepad_auto": "Опции за автоматичен избор", "gamepad_desc": "Изберете какъв вид контролер да бъде симулиран на отдалечения компютър", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "Опции за избор на DS4", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Ръчни настройки за DS4", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Колко бързо да започва повтарянето на симулираното натискане на клавишите, при задържането им в натиснато положение. Това е първоначалното закъснение в милисекунди преди да за почне повторението на клавишите.", "key_repeat_frequency": "Честота на повтаряне на клавишите", "key_repeat_frequency_desc": "Колко често да се извършват симулирани натискания на клавишите в секунда, при задържане на клавишите в натиснато положение. Стойността тук може да бъде и нецяло число.", - "key_rightalt_to_key_windows": "Изплозване на десния Alt като клавиша Windows", + "key_rightalt_to_key_win": "Карта на клавиш Alt вдясно към клавиш Windows", "key_rightalt_to_key_win_desc": "Възможно е натискането на клавиша Windows да не може да бъде изпратено към сървъра от Moonlight. В тези случаи може да настроите Sunshine да мисли, че десният Alt е клавишът Windows.", "keyboard": "Управление чрез клавиатура", "keyboard_desc": "Позволява на клиентите да управляват отдалечения компютър с клавиатура", @@ -376,6 +403,10 @@ "third_party_notice": "Забележка относно ползването на имената на трети страни" }, "troubleshooting": { + "dd_reset": "Нулиране на настройките на постоянното устройство за показване", + "dd_reset_desc": "Ако Sunshine заседне при опит да възстанови променените настройки на устройството за показване, можете да нулирате настройките и да продължите да възстановявате състоянието на дисплея ръчно.", + "dd_reset_error": "Грешка при нулиране на постоянството!", + "dd_reset_success": "Успешно нулиране на постоянството!", "force_close": "Принудително затваряне", "force_close_desc": "Ако Moonlight се оплаква, че дадено приложение работи в момента, принудителното затваряне на това приложение би трябвало да реши проблема.", "force_close_error": "Грешка при затваряне на приложението", diff --git a/src_assets/common/assets/web/public/assets/locale/de.json b/src_assets/common/assets/web/public/assets/locale/de.json index b298ebcc..3f6246b9 100644 --- a/src_assets/common/assets/web/public/assets/locale/de.json +++ b/src_assets/common/assets/web/public/assets/locale/de.json @@ -7,11 +7,13 @@ "cancel": "Abbrechen", "disabled": "Deaktiviert", "disabled_def": "Deaktiviert (Standard)", + "disabled_def_cbox": "Standard: nicht markiert", "dismiss": "Verwerfen", "do_cmd": "Befehl ausführen", "elevated": "Erhöhte", "enabled": "Aktiviert", "enabled_def": "Aktiviert (Standard)", + "enabled_def_cbox": "Standard: ausgewählt", "error": "Fehler!", "note": "Hinweis:", "password": "Passwort", @@ -150,6 +152,30 @@ "controller_desc": "Erlaubt Gästen das Host-System mit einem Gamepad/Controller zu steuern", "credentials_file": "Anmeldedaten Datei", "credentials_file_desc": "Speichere Benutzername/Passwort getrennt von Sunshine's Status-Datei.", + "dd_config_ensure_active": "Bildschirm automatisch aktivieren", + "dd_config_ensure_only_display": "Andere Displays deaktivieren und nur die angegebene Anzeige aktivieren", + "dd_config_ensure_primary": "Bildschirm automatisch aktivieren und es zur primären Anzeige machen", + "dd_config_label": "Gerätekonfiguration", + "dd_config_revert_delay": "Zurücksetzungsverzögerung konfigurieren", + "dd_config_revert_delay_desc": "Zusätzliche Verzögerung in Millisekunden, um zu warten, bevor die Konfiguration rückgängig gemacht wird, wenn die App geschlossen oder die letzte Sitzung beendet wurde. Hauptziel ist es, einen reibungsloseren Übergang beim schnellen Wechsel zwischen Apps zu ermöglichen.", + "dd_config_verify_only": "Überprüfen Sie, ob das Display aktiviert ist (Standard)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "Ein-/Ausschalten des HDR-Modus, wie vom Client gewünscht (Standard)", + "dd_hdr_option_disabled": "HDR-Einstellungen nicht ändern", + "dd_options_header": "Erweiterte Anzeigeoptionen", + "dd_refresh_rate_option": "Aktualisierungsrate", + "dd_refresh_rate_option_auto": "FPS Wert des Clients verwenden (Standard)", + "dd_refresh_rate_option_disabled": "Aktualisierungsrate nicht ändern", + "dd_refresh_rate_option_manual": "Manuell eingegebene Aktualisierungsrate verwenden", + "dd_refresh_rate_option_manual_desc": "Geben Sie die zu verwendende Aktualisierungsrate ein", + "dd_resolution_option": "Auflösung", + "dd_resolution_option_auto": "Auflösung des Clients verwenden (Standard)", + "dd_resolution_option_disabled": "Auflösung nicht ändern", + "dd_resolution_option_manual": "Manuell eingegebene Auflösung verwenden", + "dd_resolution_option_manual_desc": "Die zu verwendende Auflösung eingeben", + "dd_resolution_option_ogs_desc": "Die Option \"Spieleinstellungen optimieren\" muss auf dem Moonlight-Client aktiviert sein, damit dies funktioniert.", + "dd_wa_hdr_toggle_desc": "Wenn das virtuelle Display-Gerät als Streaming verwendet wird, könnte es eine falsche HDR-Farbe anzeigen. Wenn diese Option aktiviert ist, wird Sunshine versuchen, dieses Problem zu lindern.", + "dd_wa_hdr_toggle": "Hochkontrast-Workaround für HDR aktivieren", "ds4_back_as_touchpad_click": "Zum Touchpad-Klick zurück/auswählen", "ds4_back_as_touchpad_click_desc": "Beim Erzwingen der DS4-Emulation zum Touchpad-Klick zurück/auswählen", "encoder": "Erzwinge einen bestimmten Encoder", @@ -168,6 +194,7 @@ "gamepad_auto": "Automatische Auswahloptionen", "gamepad_desc": "Wähle welche Art von Gamepad emuliert werden soll", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "DS4 Auswahloptionen", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Manuelle DS4 Optionen", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Legen Sie fest, wie schnell sich die Tasten wiederholen. Die anfängliche Verzögerung in Millisekunden bevor Sie die Tasten wiederholen.", "key_repeat_frequency": "Tastendruck-Frequenz", "key_repeat_frequency_desc": "Wie oft Tasten jede Sekunde wiederholen. Diese konfigurierbare Option unterstützt Dezimalstellen.", - "key_rightalt_to_key_windows": "Rechter Alt-Taste auf Windows-Taste zuweisen", + "key_rightalt_to_key_win": "Rechter Alt-Taste auf Windows-Taste zuweisen", "key_rightalt_to_key_win_desc": "Möglicherweise können Sie den Windows-Schlüssel nicht direkt von Moonlight senden. In diesen Fällen kann es nützlich sein, Sunshine glauben zu lassen, dass die rechte Alt-Taste die Windows-Taste ist", "keyboard": "Tastatureingabe aktivieren", "keyboard_desc": "Erlaubt Gästen das Host-System mit der Tastatur zu steuern", @@ -376,6 +403,10 @@ "third_party_notice": "Drittanbieter-Mitteilung" }, "troubleshooting": { + "dd_reset": "Persistente Anzeigeeinstellungen zurücksetzen", + "dd_reset_desc": "Wenn Sunshine versucht, die geänderten Geräteeinstellungen wiederherzustellen, können Sie die Einstellungen zurücksetzen und den Anzeigestatus manuell wiederherstellen.", + "dd_reset_error": "Fehler beim Zurücksetzen der Persistenz!", + "dd_reset_success": "Erfolgreich zurücksetzen!", "force_close": "Schließen erzwingen", "force_close_desc": "Wenn sich Moonlight über eine aktuell laufende App beschwert, sollte das Schließen der App das Problem beheben.", "force_close_error": "Fehler beim Schließen der Anwendung", diff --git a/src_assets/common/assets/web/public/assets/locale/en_GB.json b/src_assets/common/assets/web/public/assets/locale/en_GB.json index ee7a849b..d4b30f45 100644 --- a/src_assets/common/assets/web/public/assets/locale/en_GB.json +++ b/src_assets/common/assets/web/public/assets/locale/en_GB.json @@ -7,11 +7,13 @@ "cancel": "Cancel", "disabled": "Disabled", "disabled_def": "Disabled (default)", + "disabled_def_cbox": "Default: unchecked", "dismiss": "Dismiss", "do_cmd": "Do Command", "elevated": "Elevated", "enabled": "Enabled", "enabled_def": "Enabled (default)", + "enabled_def_cbox": "Default: checked", "error": "Error!", "note": "Note:", "password": "Password", @@ -150,6 +152,30 @@ "controller_desc": "Allows guests to control the host system with a gamepad / controller", "credentials_file": "Credentials File", "credentials_file_desc": "Store Username/Password separately from Sunshine's state file.", + "dd_config_ensure_active": "Activate the display automatically", + "dd_config_ensure_only_display": "Deactivate other displays and activate only the specified display", + "dd_config_ensure_primary": "Activate the display automatically and make it a primary display", + "dd_config_label": "Device configuration", + "dd_config_revert_delay": "Config revert delay", + "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_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", + "dd_options_header": "Advanced display device options", + "dd_refresh_rate_option": "Refresh rate", + "dd_refresh_rate_option_auto": "Use FPS value provided by the client (default)", + "dd_refresh_rate_option_disabled": "Do not change refresh rate", + "dd_refresh_rate_option_manual": "Use manually entered refresh rate", + "dd_refresh_rate_option_manual_desc": "Enter the refresh rate to be used", + "dd_resolution_option": "Resolution", + "dd_resolution_option_auto": "Use resolution provided by the client (default)", + "dd_resolution_option_disabled": "Do not change resolution", + "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_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", @@ -168,6 +194,7 @@ "gamepad_auto": "Automatic selection options", "gamepad_desc": "Choose which type of gamepad to emulate on the host", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "DS4 selection options", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Manual DS4 options", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Control how fast keys will repeat themselves. The initial delay in milliseconds before repeating keys.", "key_repeat_frequency": "Key Repeat Frequency", "key_repeat_frequency_desc": "How often keys repeat every second. This configurable option supports decimals.", - "key_rightalt_to_key_windows": "Map Right Alt key to Windows key", + "key_rightalt_to_key_win": "Map Right Alt key to Windows key", "key_rightalt_to_key_win_desc": "It may be possible that you cannot send the Windows Key from Moonlight directly. In those cases it may be useful to make Sunshine think the Right Alt key is the Windows key", "keyboard": "Enable Keyboard Input", "keyboard_desc": "Allows guests to control the host system with the keyboard", @@ -376,6 +403,10 @@ "third_party_notice": "Third Party Notice" }, "troubleshooting": { + "dd_reset": "Reset Persistent Display Device Settings", + "dd_reset_desc": "If Sunshine is stuck trying to restore the changed display device settings, you can reset the settings and proceed to restore the display state manually.", + "dd_reset_error": "Error while resetting persistence!", + "dd_reset_success": "Success resetting persistence!", "force_close": "Force Close", "force_close_desc": "If Moonlight complains about an app currently running, force closing the app should fix the issue.", "force_close_error": "Error while closing Application", diff --git a/src_assets/common/assets/web/public/assets/locale/en_US.json b/src_assets/common/assets/web/public/assets/locale/en_US.json index b277c8af..968bb517 100644 --- a/src_assets/common/assets/web/public/assets/locale/en_US.json +++ b/src_assets/common/assets/web/public/assets/locale/en_US.json @@ -7,11 +7,13 @@ "cancel": "Cancel", "disabled": "Disabled", "disabled_def": "Disabled (default)", + "disabled_def_cbox": "Default: unchecked", "dismiss": "Dismiss", "do_cmd": "Do Command", "elevated": "Elevated", "enabled": "Enabled", "enabled_def": "Enabled (default)", + "enabled_def_cbox": "Default: checked", "error": "Error!", "note": "Note:", "password": "Password", @@ -150,6 +152,30 @@ "controller_desc": "Allows guests to control the host system with a gamepad / controller", "credentials_file": "Credentials File", "credentials_file_desc": "Store Username/Password separately from Sunshine's state file.", + "dd_config_ensure_active": "Activate the display automatically", + "dd_config_ensure_only_display": "Deactivate other displays and activate only the specified display", + "dd_config_ensure_primary": "Activate the display automatically and make it a primary display", + "dd_config_label": "Device configuration", + "dd_config_revert_delay": "Config revert delay", + "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_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", + "dd_options_header": "Advanced display device options", + "dd_refresh_rate_option": "Refresh rate", + "dd_refresh_rate_option_auto": "Use FPS value provided by the client (default)", + "dd_refresh_rate_option_disabled": "Do not change refresh rate", + "dd_refresh_rate_option_manual": "Use manually entered refresh rate", + "dd_refresh_rate_option_manual_desc": "Enter the refresh rate to be used", + "dd_resolution_option": "Resolution", + "dd_resolution_option_auto": "Use resolution provided by the client (default)", + "dd_resolution_option_disabled": "Do not change resolution", + "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_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", @@ -168,6 +194,7 @@ "gamepad_auto": "Automatic selection options", "gamepad_desc": "Choose which type of gamepad to emulate on the host", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "DS4 selection options", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Manual DS4 options", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Control how fast keys will repeat themselves. The initial delay in milliseconds before repeating keys.", "key_repeat_frequency": "Key Repeat Frequency", "key_repeat_frequency_desc": "How often keys repeat every second. This configurable option supports decimals.", - "key_rightalt_to_key_windows": "Map Right Alt key to Windows key", + "key_rightalt_to_key_win": "Map Right Alt key to Windows key", "key_rightalt_to_key_win_desc": "It may be possible that you cannot send the Windows Key from Moonlight directly. In those cases it may be useful to make Sunshine think the Right Alt key is the Windows key", "keyboard": "Enable Keyboard Input", "keyboard_desc": "Allows guests to control the host system with the keyboard", @@ -376,6 +403,10 @@ "third_party_notice": "Third Party Notice" }, "troubleshooting": { + "dd_reset": "Reset Persistent Display Device Settings", + "dd_reset_desc": "If Sunshine is stuck trying to restore the changed display device settings, you can reset the settings and proceed to restore the display state manually.", + "dd_reset_error": "Error while resetting persistence!", + "dd_reset_success": "Success resetting persistence!", "force_close": "Force Close", "force_close_desc": "If Moonlight complains about an app currently running, force closing the app should fix the issue.", "force_close_error": "Error while closing Application", diff --git a/src_assets/common/assets/web/public/assets/locale/es.json b/src_assets/common/assets/web/public/assets/locale/es.json index 4de5fd4a..28fc5df3 100644 --- a/src_assets/common/assets/web/public/assets/locale/es.json +++ b/src_assets/common/assets/web/public/assets/locale/es.json @@ -7,11 +7,13 @@ "cancel": "Cancelar", "disabled": "Deshabilitado", "disabled_def": "Desactivado (por defecto)", + "disabled_def_cbox": "Por defecto: sin marcar", "dismiss": "Descartar", "do_cmd": "Hacer comando", "elevated": "Elevado", "enabled": "Habilitado", "enabled_def": "Habilitado (por defecto)", + "enabled_def_cbox": "Por defecto: comprobado", "error": "¡Error!", "note": "Nota:", "password": "Contraseña", @@ -150,6 +152,30 @@ "controller_desc": "Permite a los huéspedes controlar el sistema de host con un gamepad / controlador", "credentials_file": "Archivo de credenciales", "credentials_file_desc": "Guardar nombre de usuario/contraseña por separado del archivo de estado de Sunshine.", + "dd_config_ensure_active": "Activar la pantalla automáticamente", + "dd_config_ensure_only_display": "Desactivar otras pantallas y activar sólo la pantalla especificada", + "dd_config_ensure_primary": "Activar la pantalla automáticamente y convertirla en una pantalla principal", + "dd_config_label": "Configuración del dispositivo", + "dd_config_revert_delay": "Retraso de configuración", + "dd_config_revert_delay_desc": "Retraso adicional en milisegundos a esperar antes de revertir la configuración cuando la aplicación ha sido cerrada o la última sesión ha terminado. El propósito principal es proporcionar una transición más suave cuando cambia rápidamente entre aplicaciones.", + "dd_config_verify_only": "Verificar que la pantalla está habilitada (por defecto)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "Encender/apagar el modo HDR tal como lo solicitó el cliente (por defecto)", + "dd_hdr_option_disabled": "No cambiar la configuración de HDR", + "dd_options_header": "Opciones avanzadas de dispositivo de pantalla", + "dd_refresh_rate_option": "Tasa de actualización", + "dd_refresh_rate_option_auto": "Usar valor FPS proporcionado por el cliente (por defecto)", + "dd_refresh_rate_option_disabled": "No cambiar la tasa de actualización", + "dd_refresh_rate_option_manual": "Usar manualmente la tasa de actualización introducida", + "dd_refresh_rate_option_manual_desc": "Introduzca la tasa de actualización a utilizar", + "dd_resolution_option": "Resolución", + "dd_resolution_option_auto": "Usar resolución proporcionada por el cliente (por defecto)", + "dd_resolution_option_disabled": "No cambiar resolución", + "dd_resolution_option_manual": "Usar resolución introducida manualmente", + "dd_resolution_option_manual_desc": "Introduzca la resolución a usar", + "dd_resolution_option_ogs_desc": "La opción \"Optimizar ajustes del juego\" debe estar habilitada en el cliente de luz lunar para que esto funcione.", + "dd_wa_hdr_toggle_desc": "Cuando se utiliza el dispositivo de visualización virtual como para el streaming, puede mostrar un color HDR incorrecto. Con esta opción activada, Sunshine intentará mitigar este problema.", + "dd_wa_hdr_toggle": "Habilitar solución de alto contraste para HDR", "ds4_back_as_touchpad_click": "Mapa Atrás/Seleccionar a Touchpad Clic", "ds4_back_as_touchpad_click_desc": "Al forzar la emulación DS4, mapar Atrás/Seleccionar a Touchpad Clic", "encoder": "Forzar un codificador específico", @@ -168,6 +194,7 @@ "gamepad_auto": "Opciones de selección automática", "gamepad_desc": "Elegir qué tipo de gamepad emular en el host", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "Opciones de selección DS4", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Opciones Manual de DS4", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Controla cómo se repetirán las teclas rápidas. El retardo inicial en milisegundos antes de repetir las teclas.", "key_repeat_frequency": "Frecuencia de repetición de clave", "key_repeat_frequency_desc": "Con qué frecuencia las claves se repiten cada segundo. Esta opción configurable soporta decimales.", - "key_rightalt_to_key_windows": "Asignar la tecla Alt derecha a la tecla Windows", + "key_rightalt_to_key_win": "Mapear tecla Alt Derecho a la tecla Windows", "key_rightalt_to_key_win_desc": "Es posible que no pueda enviar directamente la tecla de Windows desde Moonlight. En esos casos puede ser útil hacer que Sunshine piense que la tecla Alt correcta es la clave de Windows", "keyboard": "Activar entrada de teclado", "keyboard_desc": "Permite a los invitados controlar el sistema de host con el teclado", @@ -211,7 +238,7 @@ "log_path": "Ruta del archivo de registro", "log_path_desc": "El archivo donde se almacenan los registros actuales de Sunshine.", "min_fps_factor": "Factor FPS Mínimo", - "min_fps_factor_desc": "Sunshine usará este factor para calcular el tiempo mínimo entre fotogramas. Incrementar este valor ligeramente puede ayudar al streaming en su mayoría de contenido estático. Valores más altos consumirán más ancho de banda.", + "min_fps_factor_desc": "Sunshine usará este factor para calcular el tiempo mínimo entre fotogramas. Incrementar este valor ligeramente puede ayudar a la transmisión en su mayoría de contenido estático. Valores más altos consumirán más banda ancha.", "min_threads": "Recuento mínimo de hilos de CPU", "min_threads_desc": "Incrementar el valor reduce ligeramente la eficiencia de la codificación, pero la compensación suele valer la pena para obtener el uso de más núcleos de CPU para la codificación. El valor ideal es el valor más bajo que puede codificar de forma fiable en los ajustes de streaming deseados en su hardware.", "misc": "Opciones varias", @@ -254,7 +281,7 @@ "output_name_desc_unix": "Durante el arranque de Sunshine, debería ver la lista de pantallas detectadas. Nota: Necesita usar el valor id dentro del paréntesis.", "output_name_desc_windows": "Especifique manualmente una pantalla a usar para capturar. Si no está activada, se captura la pantalla principal. Nota: Si ha especificado un GPU arriba, esta pantalla debe estar conectada a esa GPU. Los valores apropiados se pueden encontrar usando el siguiente comando:", "output_name_unix": "Mostrar número", - "output_name_windows": "Nombre de salida", + "output_name_windows": "Mostrar Id de dispositivo", "ping_timeout": "Tiempo de espera", "ping_timeout_desc": "Cuánto tiempo esperar en milisegundos los datos de Moonlight antes de apagar la corriente", "pkey": "Clave Privada", @@ -311,7 +338,7 @@ "upnp": "UPnP", "upnp_desc": "Configurar automáticamente el reenvío de puertos para transmitir a través de Internet", "vaapi_strict_rc_buffer": "Aplicar estrictamente límites de bitrate de fotogramas para H.264/HEVC en AMD GPUs", - "vaapi_strict_rc_buffer_desc": "Activar esta opción puede evitar fotogramas soltados en la red durante los cambios de escena, pero la calidad del vídeo puede reducirse durante el movimiento.", + "vaapi_strict_rc_buffer_desc": "Activar esta opción puede evitar fotogramas sueltos/perdidos en la red durante los cambios de escena, pero la calidad del vídeo puede reducirse durante el movimiento.", "virtual_sink": "Enlace virtual", "virtual_sink_desc": "Especifique manualmente un dispositivo de audio virtual a utilizar. Si no está activado, el dispositivo se elige automáticamente. ¡Recomendamos encarecidamente dejar este campo en blanco para usar la selección automática de dispositivos!", "virtual_sink_placeholder": "Altavoces de Steam Streaming", @@ -376,6 +403,10 @@ "third_party_notice": "Aviso de Terceros" }, "troubleshooting": { + "dd_reset": "Restablecer la configuración persistente del dispositivo", + "dd_reset_desc": "Si Sunshine está atascado tratando de restaurar los ajustes cambiados del dispositivo de visualización, puede restablecer los ajustes y proceder a restaurar el estado de visualización manualmente.", + "dd_reset_error": "¡Error al restablecer la persistencia!", + "dd_reset_success": "¡Se ha restablecido la persistencia!", "force_close": "Forzar cierre", "force_close_desc": "Si Moonlight se queja de una aplicación actualmente en funcionamiento, forzar el cierre de la aplicación debería solucionar el problema.", "force_close_error": "Error al cerrar la aplicación", diff --git a/src_assets/common/assets/web/public/assets/locale/fr.json b/src_assets/common/assets/web/public/assets/locale/fr.json index a05cdfb7..74ef1ba3 100644 --- a/src_assets/common/assets/web/public/assets/locale/fr.json +++ b/src_assets/common/assets/web/public/assets/locale/fr.json @@ -7,11 +7,13 @@ "cancel": "Annuler", "disabled": "Désactivé", "disabled_def": "Désactivé (par défaut)", + "disabled_def_cbox": "Par défaut: décoché", "dismiss": "Refuser", "do_cmd": "Commande de début", "elevated": "Élevée", "enabled": "Activé", "enabled_def": "Activé (par défaut)", + "enabled_def_cbox": "Par défaut: vérifié", "error": "Erreur !", "note": "Note :", "password": "Mot de passe", @@ -150,6 +152,30 @@ "controller_desc": "Permet aux invités de contrôler le système hôte avec une manette", "credentials_file": "Fichier des identifiants", "credentials_file_desc": "Stocker le nom d'utilisateur/mot de passe séparément du fichier de données de Sunshine.", + "dd_config_ensure_active": "Activer l'affichage automatiquement", + "dd_config_ensure_only_display": "Désactiver les autres affichages et n'activer que l'affichage spécifié", + "dd_config_ensure_primary": "Activer l'affichage automatiquement et en faire un affichage principal", + "dd_config_label": "Configuration de l'appareil", + "dd_config_revert_delay": "Délai d'annulation de la configuration", + "dd_config_revert_delay_desc": "Délai supplémentaire en millisecondes à attendre avant de revenir à la configuration lorsque l'application a été fermée ou que la dernière session s'est terminée. L'objectif principal est de fournir une transition plus souple lors d'un basculement rapide entre les applications.", + "dd_config_verify_only": "Vérifiez que l'affichage est activé (par défaut)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "Activer/désactiver le mode HDR tel que demandé par le client (par défaut)", + "dd_hdr_option_disabled": "Ne pas modifier les paramètres HDR", + "dd_options_header": "Options avancées de l'appareil d'affichage", + "dd_refresh_rate_option": "Taux de rafraîchissement", + "dd_refresh_rate_option_auto": "Utiliser la valeur FPS fournie par le client (par défaut)", + "dd_refresh_rate_option_disabled": "Ne pas modifier le taux de rafraîchissement", + "dd_refresh_rate_option_manual": "Utiliser la fréquence de rafraîchissement saisie manuellement", + "dd_refresh_rate_option_manual_desc": "Entrez le taux de rafraîchissement à utiliser", + "dd_resolution_option": "Résolution", + "dd_resolution_option_auto": "Utiliser la résolution fournie par le client (par défaut)", + "dd_resolution_option_disabled": "Ne pas modifier la résolution", + "dd_resolution_option_manual": "Utiliser la résolution saisie manuellement", + "dd_resolution_option_manual_desc": "Entrez la résolution à utiliser", + "dd_resolution_option_ogs_desc": "L'option \"Optimiser les paramètres du jeu\" doit être activée sur le client Lunaire pour que cela fonctionne.", + "dd_wa_hdr_toggle_desc": "Lorsque vous utilisez un périphérique d'affichage virtuel comme pour le streaming, il peut afficher une couleur HDR incorrecte. Avec cette option activée, Sunshine essaiera d'atténuer ce problème.", + "dd_wa_hdr_toggle": "Activer la solution de contournement à haut contraste pour HDR", "ds4_back_as_touchpad_click": "Mapper Retour/Sélection au clic du pavé tactile", "ds4_back_as_touchpad_click_desc": "Lorsque vous forcez l'émulation DS4, mapper retour/sélectionner sur Touchpad Clic", "encoder": "Forcer un encodeur spécifique", @@ -168,6 +194,7 @@ "gamepad_auto": "Options de la sélection automatique", "gamepad_desc": "Choisissez le type de manette à émuler sur l'hôte", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "Options de sélection DS4", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Options manuelles pour DS4", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Contrôle la vitesse à laquelle les clés se répètent. Le délai initial en millisecondes avant de répéter les clés.", "key_repeat_frequency": "Fréquence de répétition des touches", "key_repeat_frequency_desc": "Fréquence de répétition des touches chaque seconde. Cette option configurable prend en charge les décimaux.", - "key_rightalt_to_key_windows": "Mapper la touche Alt droite à la touche Windows", + "key_rightalt_to_key_win": "Mapper la touche Alt droite à la touche Windows", "key_rightalt_to_key_win_desc": "Il est possible que vous ne puissiez pas envoyer directement la touche Windows à partir de Moonlight. Dans ce cas, il peut être utile de faire croire à Sunshine que la touche Alt droite est la touche Windows", "keyboard": "Activer l'entrée clavier", "keyboard_desc": "Permet aux invités de contrôler le système hôte avec le clavier", @@ -376,6 +403,10 @@ "third_party_notice": "Avis aux tiers" }, "troubleshooting": { + "dd_reset": "Réinitialiser les paramètres d'affichage persistant", + "dd_reset_desc": "Si Sunshine est bloqué en essayant de restaurer les paramètres de l'appareil d'affichage modifiés, vous pouvez réinitialiser les paramètres et procéder à la restauration manuelle de l'état.", + "dd_reset_error": "Erreur lors de la réinitialisation de la persistance !", + "dd_reset_success": "Récupération de la persistance de la réinitialisation!", "force_close": "Fermer de force", "force_close_desc": "Si Moonlight se plaint d'une application en cours d'exécution, forcer la fermeture de l'application devrait résoudre le problème.", "force_close_error": "Erreur lors de la fermeture de l'application", diff --git a/src_assets/common/assets/web/public/assets/locale/it.json b/src_assets/common/assets/web/public/assets/locale/it.json index 07004cb7..2e6e7150 100644 --- a/src_assets/common/assets/web/public/assets/locale/it.json +++ b/src_assets/common/assets/web/public/assets/locale/it.json @@ -7,11 +7,13 @@ "cancel": "Annulla", "disabled": "Disattivato", "disabled_def": "Disabilitato (predefinito)", + "disabled_def_cbox": "Predefinito: deselezionato", "dismiss": "Ignora", "do_cmd": "Esegui Comando", "elevated": "Come Admin", "enabled": "Abilitato", "enabled_def": "Abilitato (predefinito)", + "enabled_def_cbox": "Predefinito: controllato", "error": "Errore!", "note": "Nota:", "password": "Password", @@ -150,6 +152,30 @@ "controller_desc": "Permette ai guest di controllare il sistema host con un gamepad / controller", "credentials_file": "File Credenziali", "credentials_file_desc": "Memorizza il nome utente/password separatamente dal file di stato di Sunshine.", + "dd_config_ensure_active": "Attiva automaticamente il display", + "dd_config_ensure_only_display": "Disattiva altri display e attiva solo il display specificato", + "dd_config_ensure_primary": "Attivare automaticamente il display e renderlo uno schermo primario", + "dd_config_label": "Configurazione dispositivo", + "dd_config_revert_delay": "Ritardo ripristino configurazione", + "dd_config_revert_delay_desc": "Ulteriori ritardi in millisecondi per attendere prima di ripristinare la configurazione quando l'app è stata chiusa o l'ultima sessione è terminata. Lo scopo principale è quello di fornire una transizione più fluida quando si passa rapidamente tra le applicazioni.", + "dd_config_verify_only": "Verifica che il display sia abilitato (predefinito)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "Attiva/disattiva la modalità HDR come richiesto dal client (predefinito)", + "dd_hdr_option_disabled": "Non modificare le impostazioni HDR", + "dd_options_header": "Opzioni avanzate del dispositivo di visualizzazione", + "dd_refresh_rate_option": "Velocità di aggiornamento", + "dd_refresh_rate_option_auto": "Usa il valore FPS fornito dal client (predefinito)", + "dd_refresh_rate_option_disabled": "Non modificare la frequenza di aggiornamento", + "dd_refresh_rate_option_manual": "Usa la frequenza di aggiornamento inserita manualmente", + "dd_refresh_rate_option_manual_desc": "Inserisci la frequenza di aggiornamento da usare", + "dd_resolution_option": "Risoluzione", + "dd_resolution_option_auto": "Usa la risoluzione fornita dal client (predefinito)", + "dd_resolution_option_disabled": "Non modificare la risoluzione", + "dd_resolution_option_manual": "Usa la risoluzione inserita manualmente", + "dd_resolution_option_manual_desc": "Inserisci la risoluzione da usare", + "dd_resolution_option_ogs_desc": "L'opzione \"Ottimizza le impostazioni di gioco\" deve essere abilitata sul client Moonlight perché questo funzioni.", + "dd_wa_hdr_toggle_desc": "Quando si utilizza il dispositivo di visualizzazione virtuale come per lo streaming, potrebbe mostrare un colore HDR errato. Con questa opzione abilitata, Sunshine cercherà di mitigare questo problema.", + "dd_wa_hdr_toggle": "Abilita workaround ad alto contrasto per HDR", "ds4_back_as_touchpad_click": "Mappa Indietro/Select come Clic Touchpad", "ds4_back_as_touchpad_click_desc": "Quando si forza l'emulazione DS4, mappa Indietro/Select come Clic Touchpad", "encoder": "Forza un encoder specifico", @@ -168,6 +194,7 @@ "gamepad_auto": "Opzioni di selezione automatica", "gamepad_desc": "Scegli quale tipo di gamepad emulare sull'host", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "Opzioni di selezione DS4", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Opzioni manuali DS4", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Controlla quanto velocemente i tasti si ripeteranno. È Il ritardo iniziale in millisecondi prima di ripetere i tasti.", "key_repeat_frequency": "Frequenza Di Ripetizione Tasti", "key_repeat_frequency_desc": "Quante volte i tasti si ripetono ogni secondo. Questa opzione supporta i decimali.", - "key_rightalt_to_key_windows": "Mappare il tasto Alt destro sul tasto Windows", + "key_rightalt_to_key_win": "Mappare il tasto Alt destro sul tasto Windows", "key_rightalt_to_key_win_desc": "Potrebbe succedere che non sia possibile inviare il Tasto Windows direttamente da Moonlight. In questi casi può essere utile far credere a Sunshine che il tasto Alt Destro è il Tasto Windows", "keyboard": "Abilita Input da Tastiera", "keyboard_desc": "Consente ai guest di controllare il sistema host con la tastiera", @@ -376,6 +403,10 @@ "third_party_notice": "Avvisi di terze parti" }, "troubleshooting": { + "dd_reset": "Ripristina Impostazioni Del Dispositivo Di Visualizzazione Persistente", + "dd_reset_desc": "Se Sunshine è bloccato cercando di ripristinare le impostazioni del dispositivo di visualizzazione modificate, è possibile ripristinare le impostazioni e procedere a ripristinare lo stato di visualizzazione manualmente.", + "dd_reset_error": "Errore durante il ripristino della persistenza!", + "dd_reset_success": "Ripristino persistenza riuscito!", "force_close": "Chiusura forzata", "force_close_desc": "Se Moonlight si lamenta di un'app attualmente in esecuzione, la chiusura forzata dell'app dovrebbe risolvere il problema.", "force_close_error": "Errore durante la chiusura dell'applicazione", diff --git a/src_assets/common/assets/web/public/assets/locale/ja.json b/src_assets/common/assets/web/public/assets/locale/ja.json index e7211d6c..7ce42216 100644 --- a/src_assets/common/assets/web/public/assets/locale/ja.json +++ b/src_assets/common/assets/web/public/assets/locale/ja.json @@ -7,11 +7,13 @@ "cancel": "キャンセル", "disabled": "無効", "disabled_def": "無効 (デフォルト)", + "disabled_def_cbox": "デフォルト: 未チェック", "dismiss": "却下する", "do_cmd": "コマンド実行", "elevated": "管理者として実行", "enabled": "有効", "enabled_def": "有効 (デフォルト)", + "enabled_def_cbox": "デフォルト: checked", "error": "エラー!", "note": "メモ:", "password": "パスワード", @@ -150,6 +152,30 @@ "controller_desc": "ゲストがゲームパッド/コントローラーでホストシステムを制御できるようにします", "credentials_file": "資格情報ファイル", "credentials_file_desc": "ユーザー名/パスワードは、サンシャインのステートファイルとは別に保管してください。", + "dd_config_ensure_active": "ディスプレイを自動的に有効にする", + "dd_config_ensure_only_display": "他のディスプレイを無効にして指定したディスプレイのみ有効にする", + "dd_config_ensure_primary": "自動的にディスプレイを有効にし、プライマリディスプレイにする", + "dd_config_label": "デバイス設定", + "dd_config_revert_delay": "設定の戻す遅延時間", + "dd_config_revert_delay_desc": "アプリが閉じられたか、最後のセッションが終了したときに設定を元に戻すまで待機するミリ秒単位の追加の遅延が発生します。 主な目的は、アプリ間の迅速な切り替え時のスムーズな移行を提供することです。", + "dd_config_verify_only": "ディスプレイが有効になっていることを確認します(デフォルト)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "クライアントが要求するHDRモードのオン/オフを切り替えます (デフォルト)", + "dd_hdr_option_disabled": "HDR設定を変更しない", + "dd_options_header": "高度なディスプレイデバイスオプション", + "dd_refresh_rate_option": "リフレッシュ率", + "dd_refresh_rate_option_auto": "クライアントから提供されたFPS値を使用 (デフォルト)", + "dd_refresh_rate_option_disabled": "更新レートを変更しない", + "dd_refresh_rate_option_manual": "手動で更新レートを使用する", + "dd_refresh_rate_option_manual_desc": "使用するリフレッシュレートを入力してください", + "dd_resolution_option": "解像度", + "dd_resolution_option_auto": "クライアントから提供された解像度を使用します (デフォルト)", + "dd_resolution_option_disabled": "解像度を変更しない", + "dd_resolution_option_manual": "手動で入力した解像度を使用", + "dd_resolution_option_manual_desc": "使用する解像度を入力してください", + "dd_resolution_option_ogs_desc": "これを行うには、Moonlightクライアントで「ゲーム設定の最適化」オプションを有効にする必要があります。", + "dd_wa_hdr_toggle_desc": "ストリーミングとして仮想ディスプレイデバイスを使用すると、HDR色が正しく表示されない場合があります。このオプションを有効にすると、Sunshineはこの問題を軽減しようとします。", + "dd_wa_hdr_toggle": "HDRの高コントラスト回避を有効にする", "ds4_back_as_touchpad_click": "戻る/選択をタッチパッドにマップする", "ds4_back_as_touchpad_click_desc": "DS4エミュレーションを強制するときは、戻る/選択をタッチパッドにマップする", "encoder": "特定のエンコーダーを強制する", @@ -168,6 +194,7 @@ "gamepad_auto": "自動選択オプション", "gamepad_desc": "ホスト上でエミュレートするゲームパッドの種類を選択します", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "DS4選択オプション", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "DS4マニュアルオプション", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "キーを繰り返す速度を制御します。キーを繰り返すまでの時間をミリ秒単位で設定します。", "key_repeat_frequency": "キーの繰り返し周波数", "key_repeat_frequency_desc": "キーが毎秒繰り返される頻度。この設定可能なオプションは10進数をサポートします。", - "key_rightalt_to_key_windows": "右AltキーをWindowsキーにマップする", + "key_rightalt_to_key_win": "右AltキーをWindowsキーにマップする", "key_rightalt_to_key_win_desc": "Moonlight から Windows キーを直接送信できない可能性があります。 これらの場合、SunshineにRight AltキーがWindowsキーであると考えさせると便利かもしれません。", "keyboard": "キーボード入力を有効にする", "keyboard_desc": "ゲストがキーボードでホストシステムを制御できるようにします", @@ -376,6 +403,10 @@ "third_party_notice": "第三者通知" }, "troubleshooting": { + "dd_reset": "永続的なディスプレイデバイス設定をリセット", + "dd_reset_desc": "Sunshineが変更されたディスプレイデバイス設定を復元しようとし続けている場合は、設定をリセットして手動で表示状態を復元することができます。", + "dd_reset_error": "永続化をリセット中にエラーが発生しました!", + "dd_reset_success": "持続性のリセットに成功しました!", "force_close": "強制閉じる", "force_close_desc": "Moonlight が現在実行中のアプリについて不満がある場合、強制終了すると問題が修正されます。", "force_close_error": "アプリケーションを終了中にエラー", diff --git a/src_assets/common/assets/web/public/assets/locale/ko.json b/src_assets/common/assets/web/public/assets/locale/ko.json new file mode 100644 index 00000000..8c74a4d0 --- /dev/null +++ b/src_assets/common/assets/web/public/assets/locale/ko.json @@ -0,0 +1,438 @@ +{ + "_common": { + "apply": "적용", + "auto": "자동 설정", + "autodetect": "자동 감지 (권장)", + "beta": "(베타)", + "cancel": "취소", + "disabled": "비활성화", + "disabled_def": "사용 안 함(기본값)", + "disabled_def_cbox": "기본값: 선택 안 함", + "dismiss": "해제", + "do_cmd": "명령 수행", + "elevated": "상승", + "enabled": "활성화됨", + "enabled_def": "활성화됨 (기본값)", + "enabled_def_cbox": "기본값: 확인됨", + "error": "오류!", + "note": "참고:", + "password": "비밀번호", + "run_as": "관리자 권한으로 실행", + "save": "저장", + "see_more": "자세히 보기", + "success": "성공!", + "undo_cmd": "명령 실행 취소", + "username": "사용자 이름", + "warning": "경고!" + }, + "apps": { + "actions": "작업", + "add_cmds": "명령어 추가", + "add_new": "신규 추가", + "app_name": "애플리케이션 이름", + "app_name_desc": "Moonlight에 표시될 애플리케이션 이름", + "applications_desc": "클라이언트를 다시 시작할 때만 애플리케이션이 새로 고침 됩니다.", + "applications_title": "애플리케이션", + "auto_detach": "애플리케이션이 빠르게 종료되는 경우에도 스트리밍 계속하기", + "auto_detach_desc": "다른 프로그램이나 인스턴스를 실행한 후 빠르게 종료되는 런처형 앱을 자동으로 감지하려고 시도합니다. 런처형 앱이 감지되면 해당 앱은 분리된 앱으로 처리됩니다.", + "cmd": "명령", + "cmd_desc": "시작할 기본 애플리케이션입니다. 비어 있으면 애플리케이션이 시작되지 않습니다.", + "cmd_note": "명령 실행 파일의 경로에 공백이 포함되어 있으면 따옴표로 묶어야 합니다.", + "cmd_prep_desc": "이 애플리케이션 전/후에 실행할 명령 목록입니다. 준비 명령 중 하나라도 실패하면 애플리케이션 시작이 중단됩니다.", + "cmd_prep_name": "명령 준비", + "covers_found": "커버 발견", + "delete": "삭제", + "detached_cmds": "분리된 명령", + "detached_cmds_add": "분리된 명령 추가", + "detached_cmds_desc": "백그라운드에서 실행할 명령 목록입니다.", + "detached_cmds_note": "명령 실행 파일의 경로에 공백이 포함되어 있으면 따옴표로 묶어야 합니다.", + "edit": "편집", + "env_app_id": "앱 ID", + "env_app_name": "앱 이름", + "env_client_audio_config": "클라이언트가 요청한 오디오 구성(2.0/5.1/7.1)", + "env_client_enable_sops": "클라이언트가 최적의 스트리밍을 위해 게임 최적화 옵션을 요청했습니다(참/거짓).", + "env_client_fps": "클라이언트가 요청한 FPS(int)", + "env_client_gcmap": "요청된 게임패드 마스크, 비트셋/비트필드 형식(int)", + "env_client_hdr": "클라이언트가 HDR을 활성화합니다(참/거짓).", + "env_client_height": "클라이언트가 요청한 높이(int)", + "env_client_host_audio": "클라이언트가 호스트 오디오를 요청했습니다(참/거짓).", + "env_client_width": "클라이언트가 요청한 너비(int)", + "env_displayplacer_example": "예시 - 해상도 자동화를 위한 디스플레이플레이스:", + "env_qres_example": "예 - 해결 자동화를 위한 QR:", + "env_qres_path": "Qres 경로", + "env_var_name": "변수 이름", + "env_vars_about": "환경 변수 정보", + "env_vars_desc": "모든 명령은 기본적으로 이러한 환경 변수를 가져옵니다:", + "env_xrandr_example": "예시 - 해상도 자동화를 위한 Xrandr:", + "exit_timeout": "종료 시간 초과", + "exit_timeout_desc": "종료 요청 시 모든 앱 프로세스가 정상적으로 종료될 때까지 기다릴 시간(초)입니다. 설정하지 않으면 기본값은 최대 5초까지 대기하는 것입니다. 0 또는 음수 값으로 설정하면 앱이 즉시 종료됩니다.", + "find_cover": "표지 찾기", + "global_prep_desc": "이 애플리케이션에 대한 글로벌 준비 명령 실행을 활성화/비활성화합니다.", + "global_prep_name": "글로벌 준비 명령", + "image": "이미지", + "image_desc": "클라이언트로 전송할 애플리케이션 아이콘/사진/이미지 경로입니다. 이미지는 PNG 파일이어야 합니다. 설정하지 않으면 선샤인은 기본 상자 이미지를 전송합니다.", + "loading": "로드 중...", + "name": "이름", + "output_desc": "명령의 출력이 저장되는 파일로, 지정하지 않으면 출력이 무시됩니다.", + "output_name": "출력", + "run_as_desc": "이는 제대로 실행하려면 관리자 권한이 필요한 일부 애플리케이션에 필요할 수 있습니다.", + "wait_all": "모든 앱 프로세스가 종료될 때까지 스트리밍을 계속합니다.", + "wait_all_desc": "앱에서 시작한 모든 프로세스가 종료될 때까지 스트리밍이 계속됩니다. 이 옵션을 선택하지 않으면 다른 앱 프로세스가 계속 실행 중이더라도 초기 앱 프로세스가 종료되면 스트리밍이 중지됩니다.", + "working_dir": "작업 디렉토리", + "working_dir_desc": "프로세스에 전달할 작업 디렉터리입니다. 예를 들어 일부 애플리케이션은 작업 디렉터리를 사용하여 구성 파일을 검색합니다. 이 옵션을 설정하지 않으면 기본적으로 Sunshine은 다음 명령의 상위 디렉터리로 설정됩니다." + }, + "config": { + "adapter_name": "어댑터 이름", + "adapter_name_desc_linux_1": "캡처에 사용할 GPU를 수동으로 지정합니다.", + "adapter_name_desc_linux_2": "를 검색하여 VAAPI를 지원하는 모든 장치를 찾습니다.", + "adapter_name_desc_linux_3": "'renderD129'를 위의 장치로 바꾸면 장치의 이름과 기능이 나열됩니다. 선샤인에서 지원하려면 최소한 이 기능이 있어야 합니다:", + "adapter_name_desc_windows": "캡처에 사용할 GPU를 수동으로 지정합니다. 설정하지 않으면 GPU가 자동으로 선택됩니다. 자동 GPU 선택을 사용하려면 이 필드를 비워 두는 것이 좋습니다! 참고: 이 GPU는 디스플레이가 연결되어 있고 전원이 켜져 있어야 합니다. 적절한 값은 다음 명령을 사용하여 찾을 수 있습니다:", + "adapter_name_placeholder_windows": "Radeon RX 580 시리즈", + "add": "추가", + "address_family": "주소 가족", + "address_family_both": "IPv4+IPv6", + "address_family_desc": "선샤인이 사용하는 주소 계열 설정", + "address_family_ipv4": "IPv4 전용", + "always_send_scancodes": "항상 스캔 코드 보내기", + "always_send_scancodes_desc": "스캔코드를 전송하면 게임 및 앱과의 호환성이 향상되지만 미국식 영어 키보드 레이아웃을 사용하지 않는 특정 클라이언트에서 키보드 입력이 잘못될 수 있습니다. 특정 애플리케이션에서 키보드 입력이 전혀 작동하지 않는 경우 활성화합니다. 클라이언트의 키가 호스트에서 잘못된 입력을 생성하는 경우 비활성화합니다.", + "amd_coder": "AMF 코더(H264)", + "amd_coder_desc": "엔트로피 인코딩을 선택하여 품질 또는 인코딩 속도의 우선순위를 정할 수 있습니다. H.264만 해당.", + "amd_enforce_hrd": "AMF 가상 참조 디코더(HRD) 시행", + "amd_enforce_hrd_desc": "HRD 모델 요구 사항을 충족하기 위해 속도 제어에 대한 제약 조건을 높입니다. 이렇게 하면 비트레이트 오버플로가 크게 줄어들지만 특정 카드에서 인코딩 아티팩트 또는 품질 저하가 발생할 수 있습니다.", + "amd_preanalysis": "AMF 사전 분석", + "amd_preanalysis_desc": "이렇게 하면 속도 제어 사전 분석이 가능하므로 인코딩 지연 시간이 증가하는 대신 품질이 향상될 수 있습니다.", + "amd_quality": "AMF 품질", + "amd_quality_balanced": "균형 잡힌 -- 균형 잡힌(기본값)", + "amd_quality_desc": "인코딩 속도와 품질 간의 균형을 제어합니다.", + "amd_quality_group": "AMF 품질 설정", + "amd_quality_quality": "품질 - 품질 선호", + "amd_quality_speed": "속도 - 속도 선호", + "amd_rc": "AMF 요금 제어", + "amd_rc_cbr": "cbr - 일정한 비트레이트(HRD가 활성화된 경우 권장)", + "amd_rc_cqp": "CQP -- 상수 QP 모드", + "amd_rc_desc": "이는 클라이언트 비트레이트 목표를 초과하지 않도록 비트레이트 제어 방법을 제어합니다. 'cqp'는 비트레이트 타겟팅에 적합하지 않으며, 'vbr_latency' 이외의 다른 옵션은 비트레이트 오버플로를 제한하는 데 도움이 되는 HRD 적용에 의존합니다.", + "amd_rc_group": "AMF 속도 제어 설정", + "amd_rc_vbr_latency": "vbr_latency - 지연 시간 제한 가변 비트 전송률(HRD가 비활성화된 경우 권장, 기본값)", + "amd_rc_vbr_peak": "vbr_peak - 피크 제한 가변 비트 전송률", + "amd_usage": "AMF 사용법", + "amd_usage_desc": "기본 인코딩 프로필을 설정합니다. 아래에 제시된 모든 옵션은 사용 프로필의 하위 집합을 재정의하지만 다른 곳에서는 구성할 수 없는 숨겨진 설정이 추가로 적용됩니다.", + "amd_usage_lowlatency": "낮은 지연 시간 - 낮은 지연 시간(가장 빠름)", + "amd_usage_lowlatency_high_quality": "lowlatency_high_quality - 낮은 지연 시간, 높은 품질(빠른)", + "amd_usage_transcoding": "트랜스코딩 -- 트랜스코딩(가장 느림)", + "amd_usage_ultralowlatency": "초저지연 - 초저지연(가장 빠름, 기본값)", + "amd_usage_webcam": "웹캠 -- 웹캠(느림)", + "amd_vbaq": "AMF 분산 기반 적응형 양자화(VBAQ)", + "amd_vbaq_desc": "인간의 시각 시스템은 일반적으로 텍스처가 심한 영역의 아티팩트에 덜 민감합니다. VBAQ 모드에서는 픽셀 분산이 공간 텍스처의 복잡도를 나타내는 데 사용되므로 인코더가 더 부드러운 영역에 더 많은 비트를 할당할 수 있습니다. 이 기능을 활성화하면 일부 콘텐츠에서 주관적인 화질이 개선됩니다.", + "apply_note": "'적용'을 클릭하여 Sunshine을 다시 시작하고 변경 사항을 적용합니다. 그러면 실행 중인 모든 세션이 종료됩니다.", + "audio_sink": "오디오 싱크", + "audio_sink_desc_linux": "오디오 루프백에 사용되는 오디오 싱크의 이름입니다. 이 변수를 지정하지 않으면 pulseaudio가 기본 모니터 장치를 선택합니다. 오디오 싱크의 이름은 다음 명령을 사용하여 찾을 수 있습니다:", + "audio_sink_desc_macos": "오디오 루프백에 사용되는 오디오 싱크의 이름입니다. Sunshine은 시스템 제한으로 인해 macOS에서만 마이크에 액세스할 수 있습니다. 사운드플라워 또는 블랙홀을 사용하여 시스템 오디오를 스트리밍하려면.", + "audio_sink_desc_windows": "캡처할 특정 오디오 장치를 수동으로 지정합니다. 설정하지 않으면 장치가 자동으로 선택됩니다. 자동 장치 선택을 사용하려면 이 필드를 비워 두는 것이 좋습니다! 동일한 이름의 오디오 장치가 여러 개 있는 경우 다음 명령을 사용하여 장치 ID를 얻을 수 있습니다:", + "audio_sink_placeholder_macos": "블랙홀 2채널", + "audio_sink_placeholder_windows": "스피커(고화질 오디오 장치)", + "av1_mode": "AV1 지원", + "av1_mode_0": "선샤인은 인코더 기능에 따라 AV1 지원을 광고합니다(권장).", + "av1_mode_1": "Sunshine은 AV1에 대한 지원을 광고하지 않습니다.", + "av1_mode_2": "선샤인은 AV1 메인 8비트 프로파일 지원을 광고합니다.", + "av1_mode_3": "선샤인은 AV1 메인 8비트 및 10비트(HDR) 프로파일 지원을 광고할 예정입니다.", + "av1_mode_desc": "클라이언트가 AV1 메인 8비트 또는 10비트 비디오 스트림을 요청할 수 있습니다. AV1은 인코딩 시 CPU를 더 많이 사용하므로 이 옵션을 활성화하면 소프트웨어 인코딩을 사용할 때 성능이 저하될 수 있습니다.", + "back_button_timeout": "홈/가이드 버튼 에뮬레이션 시간 초과", + "back_button_timeout_desc": "뒤로/선택 버튼을 지정된 밀리초 동안 누르고 있으면 홈/가이드 버튼 누름이 에뮬레이션됩니다. 값을 0 미만(기본값)으로 설정하면 뒤로/선택 버튼을 길게 눌러도 홈/가이드 버튼이 에뮬레이션되지 않습니다.", + "capture": "특정 캡처 방법 강제 적용", + "capture_desc": "자동 모드에서 Sunshine은 가장 먼저 작동하는 것을 사용합니다. NvFBC에는 패치된 엔비디아 드라이버가 필요합니다.", + "cert": "인증서", + "cert_desc": "웹 UI와 Moonlight 클라이언트 페어링에 사용되는 인증서입니다. 최상의 호환성을 위해 RSA-2048 공개 키가 있어야 합니다.", + "channels": "최대 연결 클라이언트 수", + "channels_desc_1": "Sunshine을 사용하면 하나의 스트리밍 세션을 여러 클라이언트와 동시에 공유할 수 있습니다.", + "channels_desc_2": "일부 하드웨어 인코더에는 다중 스트림에서 성능을 저하시키는 제한이 있을 수 있습니다.", + "coder_cabac": "카박 - 컨텍스트 적응형 이진 산술 코딩 - 더 높은 품질", + "coder_cavlc": "cavlc - 컨텍스트 적응형 가변 길이 코딩 - 더 빠른 디코딩", + "configuration": "구성", + "controller": "게임패드 입력 활성화", + "controller_desc": "게스트가 게임패드/컨트롤러로 호스트 시스템을 제어할 수 있습니다.", + "credentials_file": "자격 증명 파일", + "credentials_file_desc": "사용자 이름/비밀번호를 Sunshine의 상태 파일과 별도로 저장하세요.", + "dd_config_ensure_active": "디스플레이 자동 활성화", + "dd_config_ensure_only_display": "다른 디스플레이를 비활성화하고 지정된 디스플레이만 활성화하기", + "dd_config_ensure_primary": "디스플레이를 자동으로 활성화하고 기본 디스플레이로 설정하기", + "dd_config_label": "장치 구성", + "dd_config_revert_delay": "구성 되돌리기 지연", + "dd_config_revert_delay_desc": "앱이 닫히거나 마지막 세션이 종료된 경우 구성을 되돌리기 전에 대기하는 추가 지연 시간(밀리초)입니다. 주요 목적은 앱 간에 빠르게 전환할 때 보다 원활한 전환을 제공하기 위한 것입니다.", + "dd_config_verify_only": "디스플레이가 활성화되어 있는지 확인합니다(기본값).", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "클라이언트의 요청에 따라 HDR 모드 켜기/끄기(기본값)", + "dd_hdr_option_disabled": "HDR 설정 변경하지 않기", + "dd_options_header": "고급 디스플레이 장치 옵션", + "dd_refresh_rate_option": "새로 고침 빈도", + "dd_refresh_rate_option_auto": "클라이언트에서 제공한 FPS 값 사용(기본값)", + "dd_refresh_rate_option_disabled": "새로 고침 빈도 변경하지 않기", + "dd_refresh_rate_option_manual": "수동으로 입력한 새로고침 빈도 사용", + "dd_refresh_rate_option_manual_desc": "사용할 새로 고침 빈도를 입력합니다.", + "dd_resolution_option": "해상도", + "dd_resolution_option_auto": "클라이언트에서 제공한 해상도 사용(기본값)", + "dd_resolution_option_disabled": "해상도 변경 금지", + "dd_resolution_option_manual": "수동으로 입력한 해상도 사용", + "dd_resolution_option_manual_desc": "사용할 해상도를 입력합니다.", + "dd_resolution_option_ogs_desc": "이 기능을 사용하려면 Moonlight 클라이언트에서 \"게임 설정 최적화\" 옵션이 활성화되어 있어야 합니다.", + "dd_wa_hdr_toggle_desc": "가상 디스플레이 장치를 스트리밍에 사용할 때 잘못된 HDR 색상이 표시될 수 있습니다. 이 옵션을 활성화하면 선샤인은 이 문제를 완화하기 위해 노력합니다.", + "dd_wa_hdr_toggle": "HDR을 위한 고대비 해결 방법 활성화", + "ds4_back_as_touchpad_click": "지도 뒤로 가기/터치패드 클릭으로 선택", + "ds4_back_as_touchpad_click_desc": "DS4 에뮬레이션을 강제할 때 뒤로/선택을 터치패드 클릭에 매핑합니다.", + "encoder": "특정 인코더 강제 적용", + "encoder_desc": "특정 인코더를 강제로 지정하지 않으면 Sunshine에서 사용 가능한 최상의 옵션을 선택합니다. 참고: Windows에서 하드웨어 인코더를 지정하는 경우 디스플레이가 연결된 GPU와 일치해야 합니다.", + "encoder_software": "소프트웨어", + "external_ip": "외부 IP", + "external_ip_desc": "외부 IP 주소가 지정되지 않은 경우 Sunshine은 자동으로 외부 IP를 감지합니다.", + "fec_percentage": "FEC 백분율", + "fec_percentage_desc": "각 비디오 프레임의 데이터 패킷당 오류를 수정하는 패킷의 백분율입니다. 값이 높을수록 더 많은 네트워크 패킷 손실을 보정할 수 있지만 대역폭 사용량이 증가합니다.", + "ffmpeg_auto": "자동 -- FFMPEG 결정에 맡김(기본값)", + "file_apps": "앱 파일", + "file_apps_desc": "현재 선샤인의 앱이 저장되어 있는 파일입니다.", + "file_state": "상태 파일", + "file_state_desc": "선샤인의 현재 상태가 저장된 파일입니다.", + "gamepad": "에뮬레이트된 게임패드 유형", + "gamepad_auto": "자동 선택 옵션", + "gamepad_desc": "호스트에서 에뮬레이트할 게임패드 유형을 선택합니다.", + "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "DS4 선택 옵션", + "gamepad_ds5": "DS5(PS5)", + "gamepad_switch": "닌텐도 프로(스위치)", + "gamepad_manual": "DS4 수동 옵션", + "gamepad_x360": "X360(Xbox 360)", + "gamepad_xone": "XOne(Xbox One)", + "global_prep_cmd": "명령 준비", + "global_prep_cmd_desc": "애플리케이션 실행 전 또는 실행 후에 실행할 명령 목록을 구성합니다. 지정된 준비 명령 중 하나라도 실패하면 애플리케이션 실행 프로세스가 중단됩니다.", + "hevc_mode": "HEVC 지원", + "hevc_mode_0": "선샤인은 인코더 기능에 따라 HEVC 지원을 광고합니다(권장).", + "hevc_mode_1": "Sunshine은 HEVC 지원을 광고하지 않습니다.", + "hevc_mode_2": "선샤인은 HEVC 메인 프로필에 대한 지원을 광고합니다.", + "hevc_mode_3": "선샤인은 HEVC 메인 및 메인10(HDR) 프로파일 지원을 광고할 예정입니다.", + "hevc_mode_desc": "클라이언트가 HEVC 메인 또는 HEVC 메인10 비디오 스트림을 요청할 수 있도록 허용합니다. HEVC는 인코딩에 CPU를 더 많이 사용하므로 이 옵션을 활성화하면 소프트웨어 인코딩을 사용할 때 성능이 저하될 수 있습니다.", + "high_resolution_scrolling": "고해상도 스크롤 지원", + "high_resolution_scrolling_desc": "이 옵션을 활성화하면 Sunshine은 Moonlight 클라이언트의 고해상도 스크롤 이벤트를 통과합니다. 고해상도 스크롤 이벤트로 너무 빠르게 스크롤하는 구형 애플리케이션의 경우 이 옵션을 비활성화하면 유용할 수 있습니다.", + "install_steam_audio_drivers": "Steam 오디오 드라이버 설치", + "install_steam_audio_drivers_desc": "Steam이 설치되어 있으면 5.1/7.1 서라운드 사운드와 호스트 오디오 음소거를 지원하는 Steam 스트리밍 스피커 드라이버가 자동으로 설치됩니다.", + "key_repeat_delay": "키 반복 지연", + "key_repeat_delay_desc": "키가 반복되는 속도를 제어합니다. 키가 반복되기 전 초기 지연 시간(밀리초)입니다.", + "key_repeat_frequency": "키 반복 빈도", + "key_repeat_frequency_desc": "매 초마다 키가 반복되는 빈도. 이 구성 가능한 옵션은 소수를 지원합니다.", + "key_rightalt_to_key_win": "오른쪽 Alt 키를 Windows 키로 매핑", + "key_rightalt_to_key_win_desc": "달빛에서 Windows 키를 직접 보낼 수 없는 경우가 있을 수 있습니다. 이러한 경우 Sunshine이 오른쪽 Alt 키를 Windows 키로 인식하도록 하는 것이 유용할 수 있습니다.", + "keyboard": "키보드 입력 활성화", + "keyboard_desc": "게스트가 키보드로 호스트 시스템을 제어할 수 있습니다.", + "lan_encryption_mode": "LAN 암호화 모드", + "lan_encryption_mode_1": "지원되는 클라이언트에서 사용 가능", + "lan_encryption_mode_2": "모든 고객에게 필수", + "lan_encryption_mode_desc": "로컬 네트워크를 통해 스트리밍할 때 암호화를 사용할 시기를 결정합니다. 암호화는 특히 성능이 낮은 호스트와 클라이언트에서 스트리밍 성능을 저하시킬 수 있습니다.", + "locale": "로캘", + "locale_desc": "Sunshine의 사용자 인터페이스에 사용되는 로캘입니다.", + "log_level": "로그 레벨", + "log_level_0": "Verbose", + "log_level_1": "Debug", + "log_level_2": "정보", + "log_level_3": "경고", + "log_level_4": "오류", + "log_level_5": "치명적", + "log_level_6": "없음", + "log_level_desc": "표준 출력으로 인쇄되는 최소 로그 수준", + "log_path": "로그 파일 경로", + "log_path_desc": "선샤인의 현재 로그가 저장된 파일입니다.", + "min_fps_factor": "최소 FPS 계수", + "min_fps_factor_desc": "선샤인은 이 계수를 사용하여 프레임 사이의 최소 시간을 계산합니다. 이 값을 약간 높이면 정적인 콘텐츠를 주로 스트리밍할 때 도움이 될 수 있습니다. 값이 높을수록 더 많은 대역폭을 소비합니다.", + "min_threads": "최소 CPU 스레드 수", + "min_threads_desc": "이 값을 높이면 인코딩 효율이 약간 떨어지지만, 일반적으로 인코딩에 더 많은 CPU 코어를 사용할 수 있다는 점에서 그만한 가치가 있습니다. 이상적인 값은 하드웨어에서 원하는 스트리밍 설정으로 안정적으로 인코딩할 수 있는 가장 낮은 값입니다.", + "misc": "기타 옵션", + "motion_as_ds4": "클라이언트 게임패드가 모션 센서가 있다고 보고하는 경우 DS4 게임패드 에뮬레이션", + "motion_as_ds4_desc": "비활성화하면 게임패드 유형을 선택할 때 모션 센서가 고려되지 않습니다.", + "mouse": "마우스 입력 활성화", + "mouse_desc": "게스트가 마우스로 호스트 시스템을 제어할 수 있습니다.", + "native_pen_touch": "기본 펜/터치 지원", + "native_pen_touch_desc": "활성화하면 선샤인은 문라이트 클라이언트의 기본 펜/터치 이벤트를 통과합니다. 이 기능은 기본 펜/터치를 지원하지 않는 구형 애플리케이션에서 비활성화하면 유용할 수 있습니다.", + "notify_pre_releases": "사전 릴리스 알림", + "notify_pre_releases_desc": "선샤인의 새 사전 출시 버전에 대한 알림 여부", + "nvenc_h264_cavlc": "H.264에서 CABAC보다 CAVLC 선호", + "nvenc_h264_cavlc_desc": "더 간단한 형태의 엔트로피 코딩. CAVLC는 동일한 화질을 위해 약 10% 더 많은 비트 전송률이 필요합니다. 아주 오래된 디코딩 장치에만 해당됩니다.", + "nvenc_latency_over_power": "전력 절감보다 낮은 인코딩 지연 시간 선호", + "nvenc_latency_over_power_desc": "선샤인은 인코딩 지연 시간을 줄이기 위해 스트리밍 중에 최대 GPU 클럭 속도를 요청합니다. 이 기능을 비활성화하면 인코딩 지연 시간이 크게 늘어날 수 있으므로 비활성화하는 것은 권장하지 않습니다.", + "nvenc_opengl_vulkan_on_dxgi": "DXGI 위에 OpenGL/Vulkan 제공", + "nvenc_opengl_vulkan_on_dxgi_desc": "Sunshine은 DXGI 위에 표시되지 않는 한 전체 화면 OpenGL 및 Vulkan 프로그램을 풀 프레임 속도로 캡처할 수 없습니다. 이는 시스템 전체 설정으로, 선샤인 프로그램 종료 시 되돌려집니다.", + "nvenc_preset": "성능 프리셋", + "nvenc_preset_1": "(가장 빠름, 기본값)", + "nvenc_preset_7": "(가장 느림)", + "nvenc_preset_desc": "수치가 높을수록 인코딩 지연 시간이 증가하는 대신 압축(주어진 비트 전송률에서의 품질)이 향상됩니다. 네트워크 또는 디코더에 의해 제한되는 경우에만 변경하는 것이 좋으며, 그렇지 않은 경우 비트 전송률을 높여도 비슷한 효과를 얻을 수 있습니다.", + "nvenc_realtime_hags": "하드웨어 가속 GPU 스케줄링에서 실시간 우선순위 사용", + "nvenc_realtime_hags_desc": "현재 NVIDIA 드라이버는 HAGS가 활성화되어 있고 실시간 우선순위가 사용되며 VRAM 사용률이 최대에 가까울 때 인코더에서 멈출 수 있습니다. 이 옵션을 비활성화하면 우선순위가 높음으로 낮아져 GPU가 과부하 상태일 때 캡처 성능이 저하되는 대신 프리즈를 방지할 수 있습니다.", + "nvenc_spatial_aq": "공간 AQ", + "nvenc_spatial_aq_desc": "동영상의 평평한 영역에 더 높은 QP 값을 할당합니다. 낮은 비트레이트에서 스트리밍할 때 활성화하는 것이 좋습니다.", + "nvenc_spatial_aq_disabled": "사용 안 함(더 빠름, 기본값)", + "nvenc_spatial_aq_enabled": "사용(느림)", + "nvenc_twopass": "투패스 모드", + "nvenc_twopass_desc": "예비 인코딩 패스를 추가합니다. 이를 통해 더 많은 모션 벡터를 감지하고 프레임 전체에 비트 전송률을 더 잘 분배하며 비트 전송률 제한을 더 엄격하게 준수할 수 있습니다. 이 기능을 비활성화하면 가끔 비트레이트 오버슈팅과 그에 따른 패킷 손실이 발생할 수 있으므로 비활성화하는 것은 권장하지 않습니다.", + "nvenc_twopass_disabled": "사용 안 함(가장 빠름, 권장하지 않음)", + "nvenc_twopass_full_res": "전체 해상도(느림)", + "nvenc_twopass_quarter_res": "분기 해상도(더 빠른, 기본값)", + "nvenc_vbv_increase": "싱글 프레임 VBV/HRD 비율 증가", + "nvenc_vbv_increase_desc": "기본적으로 선샤인은 단일 프레임 VBV/HRD를 사용하므로 인코딩된 동영상 프레임 크기가 요청된 비트레이트를 요청된 프레임 전송률로 나눈 값을 초과하지 않아야 합니다. 이 제한을 완화하면 지연 시간이 짧은 가변 비트레이트의 이점이 있지만 네트워크에 비트레이트 급증을 처리할 수 있는 버퍼 헤드룸이 없는 경우 패킷 손실이 발생할 수도 있습니다. 허용되는 최대 값은 400이며, 이는 인코딩된 동영상 프레임 상한 크기를 5배 늘린 것에 해당합니다.", + "origin_web_ui_allowed": "Origin 웹 UI 허용", + "origin_web_ui_allowed_desc": "웹 UI에 대한 액세스가 거부되지 않은 원격 엔드포인트 주소의 원본입니다.", + "origin_web_ui_allowed_lan": "LAN에 있는 사용자만 웹 UI에 액세스할 수 있습니다.", + "origin_web_ui_allowed_pc": "로컬호스트만 웹 UI에 액세스할 수 있습니다.", + "origin_web_ui_allowed_wan": "누구나 웹 UI에 액세스할 수 있습니다.", + "output_name_desc_unix": "선샤인이 시작되면 감지된 디스플레이 목록이 표시됩니다. 참고: 괄호 안의 ID 값을 사용해야 합니다. 아래는 예시이며, 실제 출력은 문제 해결 탭에서 확인할 수 있습니다.", + "output_name_desc_windows": "캡처에 사용할 디스플레이 디바이스 ID를 수동으로 지정합니다. 설정하지 않으면 기본 디스플레이가 캡처됩니다. 참고: 위에서 GPU를 지정한 경우 이 디스플레이는 해당 GPU에 연결되어 있어야 합니다. 선샤인이 시작되면 감지된 디스플레이 목록이 표시됩니다. 아래는 예시이며, 실제 출력은 문제 해결 탭에서 확인할 수 있습니다.", + "output_name_unix": "표시 번호", + "output_name_windows": "장치 ID 표시", + "ping_timeout": "핑 시간 초과", + "ping_timeout_desc": "스트림을 종료하기 전에 달빛의 데이터를 대기하는 시간(밀리초)", + "pkey": "개인 키", + "pkey_desc": "웹 UI와 Moonlight 클라이언트 페어링에 사용되는 개인 키입니다. 최상의 호환성을 위해 RSA-2048 개인키를 사용해야 합니다.", + "port": "포트", + "port_alert_1": "선샤인은 1024 이하의 포트를 사용할 수 없습니다!", + "port_alert_2": "65535 이상의 포트는 사용할 수 없습니다!", + "port_desc": "Sunshine에서 사용하는 포트 제품군 설정", + "port_http_port_note": "이 포트를 사용하여 문라이트에 연결하세요.", + "port_note": "참고", + "port_port": "포트", + "port_protocol": "프로토콜", + "port_tcp": "TCP", + "port_udp": "UDP", + "port_warning": "웹 UI를 인터넷에 노출하는 것은 보안상 위험합니다! 자신의 책임하에 진행하세요!", + "port_web_ui": "웹 UI", + "qp": "양자화 파라미터", + "qp_desc": "일부 디바이스는 고정 비트 전송률을 지원하지 않을 수 있습니다. 이러한 디바이스에서는 대신 QP가 사용됩니다. 값이 높을수록 압축률이 높아지지만 품질은 떨어집니다.", + "qsv_coder": "퀵싱크 코더(H264)", + "qsv_preset": "퀵싱크 프리셋", + "qsv_preset_fast": "빠름(낮은 품질)", + "qsv_preset_faster": "더 빠름(품질 저하)", + "qsv_preset_medium": "중간(기본값)", + "qsv_preset_slow": "느림(좋은 품질)", + "qsv_preset_slower": "느림(더 나은 품질)", + "qsv_preset_slowest": "가장 느림(최고 품질)", + "qsv_preset_veryfast": "가장 빠름(최저 품질)", + "qsv_slow_hevc": "느린 HEVC 인코딩 허용", + "qsv_slow_hevc_desc": "이렇게 하면 구형 인텔 GPU에서 HEVC 인코딩이 가능하지만, GPU 사용량이 증가하고 성능이 저하될 수 있습니다.", + "restart_note": "변경 사항을 적용하기 위해 Sunshine이 다시 시작됩니다.", + "sunshine_name": "선샤인 이름", + "sunshine_name_desc": "달빛이 표시하는 이름입니다. 지정하지 않으면 PC의 호스트 이름이 사용됩니다.", + "sw_preset": "SW 프리셋", + "sw_preset_desc": "인코딩 속도(초당 인코딩 프레임 수)와 압축 효율성(비트스트림의 비트당 품질) 간의 절충점을 최적화합니다. 기본값은 초고속입니다.", + "sw_preset_fast": "빠른", + "sw_preset_faster": "더 빠르게", + "sw_preset_medium": "medium", + "sw_preset_slow": "slow", + "sw_preset_slower": "느린", + "sw_preset_superfast": "초고속(기본값)", + "sw_preset_ultrafast": "초고속", + "sw_preset_veryfast": "매우 빠름", + "sw_preset_veryslow": "매우 느림", + "sw_tune": "SW Tune", + "sw_tune_animation": "애니메이션 - 만화에 적합하며 더 높은 디블럭킹과 더 많은 기준 프레임을 사용합니다.", + "sw_tune_desc": "사전 설정 후에 적용되는 튜닝 옵션입니다. 기본값은 제로 레이턴시입니다.", + "sw_tune_fastdecode": "fastdecode - 특정 필터를 비활성화하여 더 빠르게 디코딩할 수 있습니다.", + "sw_tune_film": "영화 - 고품질 영화 콘텐츠에 사용, 차단 해제 감소", + "sw_tune_grain": "그레인 - 오래되고 거친 필름 소재의 그레인 구조를 보존합니다.", + "sw_tune_stillimage": "스틸 이미지 - 슬라이드쇼와 같은 콘텐츠에 적합", + "sw_tune_zerolatency": "제로 레이턴시 - 빠른 인코딩 및 저지연 스트리밍에 적합(기본값)", + "touchpad_as_ds4": "클라이언트 게임패드가 터치패드가 있다고 보고하는 경우 DS4 게임패드 에뮬레이션", + "touchpad_as_ds4_desc": "비활성화하면 게임패드 유형을 선택할 때 터치패드의 존재 여부가 고려되지 않습니다.", + "upnp": "UPnP", + "upnp_desc": "인터넷을 통한 스트리밍을 위한 포트 포워딩 자동 구성", + "vaapi_strict_rc_buffer": "AMD GPU에서 H.264/HEVC에 대한 프레임 비트레이트 제한을 엄격하게 적용합니다.", + "vaapi_strict_rc_buffer_desc": "이 옵션을 활성화하면 장면이 변경되는 동안 네트워크를 통해 프레임이 끊기는 것을 방지할 수 있지만 움직이는 동안 동영상 품질이 저하될 수 있습니다.", + "virtual_sink": "가상 싱크", + "virtual_sink_desc": "사용할 가상 오디오 장치를 수동으로 지정합니다. 설정하지 않으면 장치가 자동으로 선택됩니다. 자동 장치 선택을 사용하려면 이 필드를 비워 두는 것이 좋습니다!", + "virtual_sink_placeholder": "Steam 스트리밍 스피커", + "vt_coder": "비디오 툴박스 코더", + "vt_realtime": "비디오툴박스 실시간 인코딩", + "vt_software": "비디오툴박스 소프트웨어 인코딩", + "vt_software_allowed": "허용됨", + "vt_software_forced": "강제", + "wan_encryption_mode": "WAN 암호화 모드", + "wan_encryption_mode_1": "지원되는 클라이언트에 사용(기본값)", + "wan_encryption_mode_2": "모든 고객에게 필수", + "wan_encryption_mode_desc": "인터넷을 통해 스트리밍할 때 암호화를 사용할 시기를 결정합니다. 암호화는 특히 성능이 낮은 호스트와 클라이언트에서 스트리밍 성능을 저하시킬 수 있습니다." + }, + "index": { + "description": "선샤인은 문라이트의 자체 호스팅 게임 스트림 호스트입니다.", + "download": "다운로드", + "installed_version_not_stable": "선샤인 시험판 버전을 실행 중입니다. 버그나 기타 문제가 발생할 수 있습니다. 문제가 발생하면 신고해 주세요. Sunshine을 더 나은 소프트웨어로 만드는 데 도움을 주셔서 감사합니다!", + "loading_latest": "최신 릴리스 로드 중...", + "new_pre_release": "새로운 사전 출시 버전이 출시되었습니다!", + "new_stable": "새로운 안정 버전이 출시되었습니다!", + "startup_errors": "주의! 시작 중에 이러한 오류가 감지되었습니다. 스트리밍하기 전에 이 오류를 수정할 것을 강력히 권장합니다.", + "version_dirty": "선샤인이 더 나은 소프트웨어가 될 수 있도록 도와주셔서 감사합니다!", + "version_latest": "최신 버전의 Sunshine을 실행 중입니다.", + "welcome": "안녕하세요, 선샤인!" + }, + "navbar": { + "applications": "애플리케이션", + "configuration": "구성", + "home": "홈", + "password": "비밀번호 변경", + "pin": "핀", + "theme_auto": "자동", + "theme_dark": "Dark", + "theme_light": "빛", + "toggle_theme": "테마", + "troubleshoot": "문제 해결" + }, + "password": { + "confirm_password": "비밀번호 확인", + "current_creds": "현재 자격 증명", + "new_creds": "새 자격 증명", + "new_username_desc": "지정하지 않으면 사용자 아이디가 변경되지 않습니다.", + "password_change": "비밀번호 변경", + "success_msg": "비밀번호가 성공적으로 변경되었습니다! 이 페이지가 곧 다시 로드되며 브라우저에서 새 자격 증명을 입력하라는 메시지가 표시됩니다." + }, + "pin": { + "device_name": "장치 이름", + "pair_failure": "페어링에 실패했습니다: PIN이 올바르게 입력되었는지 확인하세요.", + "pair_success": "성공! 계속하려면 달빛을 확인해 주세요.", + "pin_pairing": "PIN 페어링", + "send": "보내기", + "warning_msg": "페어링하려는 클라이언트에 대한 액세스 권한이 있는지 확인하세요. 이 소프트웨어는 컴퓨터를 완전히 제어할 수 있으므로 주의하세요!" + }, + "resource_card": { + "github_discussions": "GitHub 토론", + "legal": "법률", + "legal_desc": "이 소프트웨어를 계속 사용함으로써 귀하는 다음 문서의 이용 약관에 동의하게 됩니다.", + "license": "라이선스", + "lizardbyte_website": "리자드바이트 웹사이트", + "resources": "리소스", + "resources_desc": "선샤인을 위한 리소스!", + "third_party_notice": "제3자 고지" + }, + "troubleshooting": { + "dd_reset": "영구 디스플레이 장치 설정 재설정", + "dd_reset_desc": "변경된 디스플레이 장치 설정을 복원하는 데 문제가 있는 경우 설정을 재설정하고 수동으로 디스플레이 상태 복원을 진행할 수 있습니다.", + "dd_reset_error": "지속성을 재설정하는 동안 오류가 발생했습니다!", + "dd_reset_success": "지속성 재설정에 성공했습니다!", + "force_close": "강제 종료", + "force_close_desc": "현재 실행 중인 앱에 대해 문라이트가 불만을 제기하는 경우 앱을 강제 종료하면 문제가 해결됩니다.", + "force_close_error": "애플리케이션을 닫는 동안 오류가 발생했습니다.", + "force_close_success": "신청이 성공적으로 마감되었습니다!", + "logs": "로그", + "logs_desc": "Sunshine이 업로드한 로그 보기", + "logs_find": "찾기...", + "restart_sunshine": "선샤인 다시 시작", + "restart_sunshine_desc": "Sunshine이 제대로 작동하지 않는다면 다시 시작해 보세요. 그러면 실행 중인 모든 세션이 종료됩니다.", + "restart_sunshine_success": "선샤인이 다시 시작됩니다.", + "troubleshooting": "문제 해결", + "unpair_all": "모두 페어링 해제", + "unpair_all_error": "페어링 해제 중 오류", + "unpair_all_success": "페어링되지 않은 모든 장치.", + "unpair_desc": "페어링된 장치를 제거합니다. 활성 세션이 있는 개별적으로 페어링되지 않은 장치는 연결된 상태로 유지되지만 세션을 시작하거나 다시 시작할 수는 없습니다.", + "unpair_single_no_devices": "페어링된 장치가 없습니다.", + "unpair_single_success": "그러나 디바이스가 여전히 활성 세션에 있을 수 있습니다. 열려 있는 세션을 종료하려면 위의 '강제 종료' 버튼을 사용하세요.", + "unpair_single_unknown": "알 수 없는 클라이언트", + "unpair_title": "장치 페어링 해제" + }, + "welcome": { + "confirm_password": "비밀번호 확인", + "create_creds": "시작하기 전에 웹 UI에 액세스하기 위한 새 사용자 아이디와 비밀번호를 만들어야 합니다.", + "create_creds_alert": "선샤인의 웹 UI에 액세스하려면 아래 자격 증명이 필요합니다. 다시는 볼 수 없으니 안전하게 보관하세요!", + "greeting": "선샤인에 오신 것을 환영합니다!", + "login": "로그인", + "welcome_success": "이 페이지가 곧 다시 로드되며 브라우저에서 새 자격 증명을 입력하라는 메시지가 표시됩니다." + } +} diff --git a/src_assets/common/assets/web/public/assets/locale/pl.json b/src_assets/common/assets/web/public/assets/locale/pl.json index c0ed0b35..a6efd0a2 100644 --- a/src_assets/common/assets/web/public/assets/locale/pl.json +++ b/src_assets/common/assets/web/public/assets/locale/pl.json @@ -7,11 +7,13 @@ "cancel": "Anuluj", "disabled": "Wyłączony", "disabled_def": "Wyłączone (domyślnie)", + "disabled_def_cbox": "Domyślnie: odznaczone", "dismiss": "Odrzuć", "do_cmd": "Polecenie Do", "elevated": "Podwyższone", "enabled": "Włączone", "enabled_def": "Włączone (domyślnie)", + "enabled_def_cbox": "Domyślnie: zaznaczone", "error": "Błąd!", "note": "Uwaga:", "password": "Hasło", @@ -150,6 +152,30 @@ "controller_desc": "Umożliwia gościom kontrolowanie systemu hosta za pomocą gamepada / kontrolera", "credentials_file": "Plik poświadczeń", "credentials_file_desc": "Przechowuj nazwę użytkownika/hasło oddzielnie od pliku stanu Sunshine.", + "dd_config_ensure_active": "Automatycznie aktywuj wyświetlanie", + "dd_config_ensure_only_display": "Dezaktywuj inne wyświetlacze i aktywuj tylko określony wyświetlacz", + "dd_config_ensure_primary": "Aktywuj ekran automatycznie i spraw, by był głównym wyświetlaczem", + "dd_config_label": "Konfiguracja urządzenia", + "dd_config_revert_delay": "Opóźnienie przywrócenia konfiguracji", + "dd_config_revert_delay_desc": "Dodatkowe opóźnienie w milisekundach, aby poczekać przed przywróceniem konfiguracji po zamknięciu aplikacji lub zakończeniu ostatniej sesji. Głównym celem jest zapewnienie płynniejszego przejścia przy szybkiej zmianie pomiędzy aplikacjami.", + "dd_config_verify_only": "Sprawdź, czy wyświetlacz jest włączony (domyślnie)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "Włącz/wyłącz tryb HDR zgodnie z żądaniem klienta (domyślnie)", + "dd_hdr_option_disabled": "Nie zmieniaj ustawień HDR", + "dd_options_header": "Zaawansowane opcje wyświetlania urządzenia", + "dd_refresh_rate_option": "Częstotliwość odświeżania", + "dd_refresh_rate_option_auto": "Użyj wartości FPS dostarczonej przez klienta (domyślnie)", + "dd_refresh_rate_option_disabled": "Nie zmieniaj szybkości odświeżania", + "dd_refresh_rate_option_manual": "Użyj ręcznie wprowadzonej prędkości odświeżania", + "dd_refresh_rate_option_manual_desc": "Wprowadź szybkość odświeżania do użycia", + "dd_resolution_option": "Rozdzielczość", + "dd_resolution_option_auto": "Użyj rozdzielczości dostarczonej przez klienta (domyślnie)", + "dd_resolution_option_disabled": "Nie zmieniaj rozdzielczości", + "dd_resolution_option_manual": "Użyj ręcznie wprowadzonej rozdzielczości", + "dd_resolution_option_manual_desc": "Wprowadź rozdzielczość do użycia", + "dd_resolution_option_ogs_desc": "Opcja \"Optymalizacja ustawień gry\" musi być włączona w kliencie Księżyca, aby to działało.", + "dd_wa_hdr_toggle_desc": "Gdy używasz wirtualnego urządzenia wyświetlającego jako streamingu, może wyświetlać niepoprawny kolor HDR. W tej opcji Sunshine spróbuje złagodzić ten problem.", + "dd_wa_hdr_toggle": "Włącz obsługę wysokiego kontrastu dla HDR", "ds4_back_as_touchpad_click": "Mapuj przycisk Wstecz/Wybierz na kliknięcie panelu dotykowego", "ds4_back_as_touchpad_click_desc": "Podczas wymuszania emulacji DS4, mapuj Back/Select na kliknięcie panelu dotykowego", "encoder": "Wymuś określony koder", @@ -168,6 +194,7 @@ "gamepad_auto": "Opcje automatycznego wyboru", "gamepad_desc": "Wybierz typ kontrolera, który ma być emulowany na hoście", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "Opcje wyboru DS4", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Ustawienia DS4", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Kontroluje szybkość powtarzania klawiszy. Początkowe opóźnienie w milisekundach przed powtarzaniem klawiszy.", "key_repeat_frequency": "Częstotliwość powtarzania klawiszy", "key_repeat_frequency_desc": "Jak często klawisze powtarzają się co sekundę. Ta konfigurowalna opcja obsługuje wartości dziesiętne.", - "key_rightalt_to_key_windows": "Mapuj klawisz prawy Alt na klawisz Windows", + "key_rightalt_to_key_win": "Przycisk Mapy Prawy Alt do klucza Windows", "key_rightalt_to_key_win_desc": "Może się zdarzyć, że nie można wysłać klawisza Windows bezpośrednio z Moonlight. W takich przypadkach przydatne może być sprawienie, by Sunshine myślał, że prawy Alt jest klawiszem Windows", "keyboard": "Włącz wejście klawiatury", "keyboard_desc": "Umożliwia gościom kontrolowanie systemu hosta za pomocą klawiatury", @@ -376,6 +403,10 @@ "third_party_notice": "Powiadomienia strony trzeciej" }, "troubleshooting": { + "dd_reset": "Resetuj stałe ustawienia wyświetlacza", + "dd_reset_desc": "Jeśli Sunshine utknie próbując przywrócić zmienione ustawienia wyświetlacza, możesz zresetować ustawienia i ręcznie przywrócić stan wyświetlacza.", + "dd_reset_error": "Błąd podczas resetowania trwałości!", + "dd_reset_success": "Pomyślnie resetowano trwałość!", "force_close": "Wymuś zamknięcie", "force_close_desc": "Jeśli Moonlight skarży się na aktualnie uruchomioną aplikację, wymuszenie jej zamknięcia powinno rozwiązać problem.", "force_close_error": "Błąd podczas zamykania aplikacji", diff --git a/src_assets/common/assets/web/public/assets/locale/pt.json b/src_assets/common/assets/web/public/assets/locale/pt.json index 96ae44cb..73418cd9 100644 --- a/src_assets/common/assets/web/public/assets/locale/pt.json +++ b/src_assets/common/assets/web/public/assets/locale/pt.json @@ -7,11 +7,13 @@ "cancel": "Cancelar", "disabled": "Desabilitado", "disabled_def": "Desativado (padrão)", + "disabled_def_cbox": "Padrão: desmarcado", "dismiss": "Descartar", "do_cmd": "Faça o Comando", "elevated": "Elevado", "enabled": "Ativado", "enabled_def": "Ativado (padrão)", + "enabled_def_cbox": "Padrão: marcado", "error": "Erro!", "note": "Nota:", "password": "Palavra-passe", @@ -150,6 +152,30 @@ "controller_desc": "Permite que os convidados controlem o sistema de host com controle / controle do gamepad", "credentials_file": "Arquivo de credenciais", "credentials_file_desc": "Armazenar Usuário/Senha separadamente do arquivo de estado da Sunshine.", + "dd_config_ensure_active": "Ativar a tela automaticamente", + "dd_config_ensure_only_display": "Desativar outras exibições e ativar somente a exibição especificada", + "dd_config_ensure_primary": "Ativar a tela automaticamente e torná-la uma tela primária", + "dd_config_label": "Configuração do dispositivo", + "dd_config_revert_delay": "Configurar atraso de reverter", + "dd_config_revert_delay_desc": "Atraso adicional em milissegundos para esperar antes de reverter a configuração quando o aplicativo for fechado ou a última sessão for encerrada. O principal é proporcionar uma transição mais suave ao alternar rapidamente entre aplicativos.", + "dd_config_verify_only": "Verifique se a tela está ativada (padrão)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "Ligar/desligar o modo HDR conforme solicitado pelo cliente (padrão)", + "dd_hdr_option_disabled": "Não alterar as configurações do HDR", + "dd_options_header": "Opções avançadas do dispositivo", + "dd_refresh_rate_option": "Taxa de atualização", + "dd_refresh_rate_option_auto": "Usar valor de FPS fornecido pelo cliente (padrão)", + "dd_refresh_rate_option_disabled": "Não alterar a taxa de atualização", + "dd_refresh_rate_option_manual": "Usar taxa de atualização digitada manualmente", + "dd_refresh_rate_option_manual_desc": "Digite a taxa de atualização a ser utilizada", + "dd_resolution_option": "Resolução:", + "dd_resolution_option_auto": "Resolução de uso fornecida pelo cliente (padrão)", + "dd_resolution_option_disabled": "Não alterar a resolução", + "dd_resolution_option_manual": "Usar resolução inserida manualmente", + "dd_resolution_option_manual_desc": "Digite a resolução a ser usada", + "dd_resolution_option_ogs_desc": "A opção \"Otimizar configurações do jogo\" deve estar ativada no cliente do Luar para que isto funcione.", + "dd_wa_hdr_toggle_desc": "Ao usar o dispositivo de exibição virtual para streaming, ele pode exibir uma cor HDR incorreta. Com esta opção ativada, o Sunshine tentará mitigar esse problema.", + "dd_wa_hdr_toggle": "Ativar solução alternativa de alto contraste para HDR", "ds4_back_as_touchpad_click": "Mapear Voltar/Selecionar para o Touchpad Clique", "ds4_back_as_touchpad_click_desc": "Ao forçar a emulação do DS4, selecione um Voltar/Selecione para o Touchpad Clique", "encoder": "Forçar um Codificador Específico", @@ -168,6 +194,7 @@ "gamepad_auto": "Opções de seleção automáticas", "gamepad_desc": "Escolha qual tipo de controle será emulado no host", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "Opções de seleção DS4", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Opções de DS4 manual", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Controla a rapidez com que as teclas se irão repetir. O atraso inicial em milissegundos antes de repetir as chaves.", "key_repeat_frequency": "Frequência de repetição de chave", "key_repeat_frequency_desc": "Com que frequência as chaves se repetem a cada segundo. Esta opção configurável suporta decimais.", - "key_rightalt_to_key_windows": "Tecla Alt Right Map para a tecla Windows", + "key_rightalt_to_key_win": "Tecla Alt Right Map para a tecla Windows", "key_rightalt_to_key_win_desc": "É possível que você não possa enviar diretamente a chave Windows do Moonlight. Nesses casos, pode ser útil fazer Sunshine pensar que a tecla Alt direita é a tecla Windows", "keyboard": "Habilitar Entrada de Teclado", "keyboard_desc": "Permite aos convidados controlar o sistema de host com o teclado", @@ -376,6 +403,10 @@ "third_party_notice": "Aviso de terceiros" }, "troubleshooting": { + "dd_reset": "Redefinir Configurações do Dispositivo de Exibição Persistente", + "dd_reset_desc": "Se o Sunshine estiver preso tentando restaurar as configurações alteradas do dispositivo de exibição, você pode redefinir as configurações e prosseguir para restaurar o estado da exibição manualmente.", + "dd_reset_error": "Erro ao redefinir a persistência!", + "dd_reset_success": "Sucesso ao redefinir a persistência!", "force_close": "Forçar fechamento", "force_close_desc": "Se o Moonlight reclamar de um aplicativo em execução, forçar o fechamento do aplicativo deve resolver o problema.", "force_close_error": "Erro ao fechar o aplicativo", diff --git a/src_assets/common/assets/web/public/assets/locale/pt_BR.json b/src_assets/common/assets/web/public/assets/locale/pt_BR.json index cf5755a2..f8cf6117 100644 --- a/src_assets/common/assets/web/public/assets/locale/pt_BR.json +++ b/src_assets/common/assets/web/public/assets/locale/pt_BR.json @@ -7,11 +7,13 @@ "cancel": "Cancelar", "disabled": "Desativado", "disabled_def": "Desativado (padrão)", + "disabled_def_cbox": "Padrão: desmarcado", "dismiss": "Dispensar", "do_cmd": "Comando Do", "elevated": "Elevado", "enabled": "Ativado", "enabled_def": "Ativado (padrão)", + "enabled_def_cbox": "Padrão: marcado", "error": "Erro!", "note": "Observação:", "password": "Senha", @@ -150,6 +152,30 @@ "controller_desc": "Permite que os convidados controlem o sistema host com um gamepad/controlador", "credentials_file": "Arquivo de credenciais", "credentials_file_desc": "Armazene o nome de usuário/senha separadamente do arquivo de estado do Sunshine.", + "dd_config_ensure_active": "Ativar a tela automaticamente", + "dd_config_ensure_only_display": "Desativar outras exibições e ativar somente a exibição especificada", + "dd_config_ensure_primary": "Ativar a tela automaticamente e torná-la uma tela primária", + "dd_config_label": "Configuração do dispositivo", + "dd_config_revert_delay": "Configurar atraso de reverter", + "dd_config_revert_delay_desc": "Atraso adicional em milissegundos para esperar antes de reverter a configuração quando o aplicativo for fechado ou a última sessão for encerrada. O principal é proporcionar uma transição mais suave ao alternar rapidamente entre aplicativos.", + "dd_config_verify_only": "Verifique se a tela está ativada (padrão)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "Ligar/desligar o modo HDR conforme solicitado pelo cliente (padrão)", + "dd_hdr_option_disabled": "Não alterar as configurações do HDR", + "dd_options_header": "Opções avançadas do dispositivo", + "dd_refresh_rate_option": "Taxa de atualização", + "dd_refresh_rate_option_auto": "Usar valor de FPS fornecido pelo cliente (padrão)", + "dd_refresh_rate_option_disabled": "Não alterar a taxa de atualização", + "dd_refresh_rate_option_manual": "Usar taxa de atualização digitada manualmente", + "dd_refresh_rate_option_manual_desc": "Digite a taxa de atualização a ser utilizada", + "dd_resolution_option": "Resolução:", + "dd_resolution_option_auto": "Resolução de uso fornecida pelo cliente (padrão)", + "dd_resolution_option_disabled": "Não alterar a resolução", + "dd_resolution_option_manual": "Usar resolução inserida manualmente", + "dd_resolution_option_manual_desc": "Digite a resolução a ser usada", + "dd_resolution_option_ogs_desc": "A opção \"Otimizar configurações do jogo\" deve estar ativada no cliente do Luar para que isto funcione.", + "dd_wa_hdr_toggle_desc": "Ao usar o dispositivo de exibição virtual para streaming, ele pode exibir uma cor HDR incorreta. Com esta opção ativada, o Sunshine tentará mitigar esse problema.", + "dd_wa_hdr_toggle": "Ativar solução alternativa de alto contraste para HDR", "ds4_back_as_touchpad_click": "Mapear Voltar/Selecionar para clicar no touchpad", "ds4_back_as_touchpad_click_desc": "Ao forçar a emulação DS4, mapeie Back/Select para Touchpad Click", "encoder": "Forçar um codificador específico", @@ -168,6 +194,7 @@ "gamepad_auto": "Opções de seleção automática", "gamepad_desc": "Escolha o tipo de gamepad a ser emulado no host", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "Opções de seleção DS4", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Opções manuais do DS4", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Controle a velocidade com que as teclas se repetirão. O atraso inicial em milissegundos antes da repetição das teclas.", "key_repeat_frequency": "Frequência de repetição da tecla", "key_repeat_frequency_desc": "A frequência com que as teclas se repetem a cada segundo. Essa opção configurável aceita decimais.", - "key_rightalt_to_key_windows": "Mapear a tecla Alt direita para a tecla Windows", + "key_rightalt_to_key_win": "Tecla Alt Right Map para a tecla Windows", "key_rightalt_to_key_win_desc": "Pode ser que você não consiga enviar a tecla Windows diretamente do Moonlight. Nesses casos, pode ser útil fazer com que o Sunshine pense que a tecla Alt. direita é a tecla Windows", "keyboard": "Ativar entrada de teclado", "keyboard_desc": "Permite que os convidados controlem o sistema host com o teclado", @@ -376,6 +403,10 @@ "third_party_notice": "Aviso de terceiros" }, "troubleshooting": { + "dd_reset": "Redefinir Configurações do Dispositivo de Exibição Persistente", + "dd_reset_desc": "Se o Sunshine estiver preso tentando restaurar as configurações alteradas do dispositivo de exibição, você pode redefinir as configurações e prosseguir para restaurar o estado da exibição manualmente.", + "dd_reset_error": "Erro ao redefinir a persistência!", + "dd_reset_success": "Sucesso ao redefinir a persistência!", "force_close": "Forçar fechamento", "force_close_desc": "Se o Moonlight reclamar de um aplicativo em execução, forçar o fechamento do aplicativo deve corrigir o problema.", "force_close_error": "Erro ao fechar o aplicativo", diff --git a/src_assets/common/assets/web/public/assets/locale/ru.json b/src_assets/common/assets/web/public/assets/locale/ru.json index c0347702..d71bd7e5 100644 --- a/src_assets/common/assets/web/public/assets/locale/ru.json +++ b/src_assets/common/assets/web/public/assets/locale/ru.json @@ -7,11 +7,13 @@ "cancel": "Отмена", "disabled": "Отключено", "disabled_def": "Отключено (по умолчанию)", + "disabled_def_cbox": "По умолчанию: снято", "dismiss": "Отклонить", "do_cmd": "Команда запуска", "elevated": "Требуются", "enabled": "Включено", "enabled_def": "Включено (по умолчанию)", + "enabled_def_cbox": "По умолчанию: проверено", "error": "Ошибка!", "note": "Примечание:", "password": "Пароль", @@ -150,6 +152,30 @@ "controller_desc": "Позволяет гостям контролировать хост-систему с помощью геймпада / контроллера", "credentials_file": "Файл учётных данных", "credentials_file_desc": "Храните имя пользователя/пароль отдельно от файла состояния Sunshine.", + "dd_config_ensure_active": "Активировать экран автоматически", + "dd_config_ensure_only_display": "Отключить другие дисплеи и активировать только указанный дисплей", + "dd_config_ensure_primary": "Активировать экран автоматически и сделать его основным дисплеем", + "dd_config_label": "Конфигурация устройства", + "dd_config_revert_delay": "Задержка отката конфигурации", + "dd_config_revert_delay_desc": "Дополнительная задержка в миллисекундах перед откатом конфигурации приложения или последней сессии прервана. Главная цель - обеспечить более плавный переход при быстром переключении между приложениями.", + "dd_config_verify_only": "Проверьте, включен ли дисплей (по умолчанию)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "Включение/выключение режима HDR по требованию клиента (по умолчанию)", + "dd_hdr_option_disabled": "Не изменять настройки HDR", + "dd_options_header": "Расширенные настройки устройства", + "dd_refresh_rate_option": "Частота обновления", + "dd_refresh_rate_option_auto": "Использовать значение FPS (по умолчанию)", + "dd_refresh_rate_option_disabled": "Не изменять частоту обновления", + "dd_refresh_rate_option_manual": "Использовать вручную введенную частоту обновления", + "dd_refresh_rate_option_manual_desc": "Введите частоту обновления для использования", + "dd_resolution_option": "Разрешение", + "dd_resolution_option_auto": "Использовать разрешение, предоставляемое клиентом (по умолчанию)", + "dd_resolution_option_disabled": "Не изменять разрешение", + "dd_resolution_option_manual": "Использовать вручную введенное разрешение", + "dd_resolution_option_manual_desc": "Введите разрешение, которое будет использовано", + "dd_resolution_option_ogs_desc": "Для этого необходимо включить опцию \"Оптимизация настроек игры\" на клиенте Moonlight.", + "dd_wa_hdr_toggle_desc": "При использовании виртуального устройства отображения для потокового воспроизведения может отображаться неправильный цвет HDR. При включенной опции Sunshine попытается уменьшить эту проблему.", + "dd_wa_hdr_toggle": "Включить высококонтрастный обход для HDR", "ds4_back_as_touchpad_click": "Назад/Выберете для нажатия сенсорной панели", "ds4_back_as_touchpad_click_desc": "При принудительной эмуляции DS4, нажмите на карточку Назад/Выделение для сенсорной панели", "encoder": "Принудительный кодировщик", @@ -168,6 +194,7 @@ "gamepad_auto": "Настройка автоматического выбора", "gamepad_desc": "Выберите тип геймпада для эмулирования на хосте", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "Параметры выбора DS4", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Ручные настройки DS4", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Контролируйте как быстрые клавиши будут повторяться. Начальная задержка в миллисекундах до повторных нажатий.", "key_repeat_frequency": "Частота повторения нажатий", "key_repeat_frequency_desc": "Как часто нажатия повторяются за секунду. Эта настройка поддерживает десятичные дроби.", - "key_rightalt_to_key_windows": "Переопределить клавишу правый Alt как клавишу Windows", + "key_rightalt_to_key_win": "Карта клавиши Alt справа для клавиши Windows", "key_rightalt_to_key_win_desc": "Возможно, вы не можете послать нажатие кнопки Windows непосредственно из Moonlight. В таком случае, полезно чтобы Sunshine думал, что клавиша правый Alt является клавишей Windows", "keyboard": "Включить ввод с клавиатуры", "keyboard_desc": "Позволяет гостям управлять системой хоста с помощью клавиатуры", @@ -376,6 +403,10 @@ "third_party_notice": "Уведомление о третьих сторонах" }, "troubleshooting": { + "dd_reset": "Сбросить настройки постоянного дисплея устройства", + "dd_reset_desc": "Если Sunshine пытается восстановить измененные настройки дисплея, вы можете сбросить настройки и продолжить восстановление состояния дисплея вручную.", + "dd_reset_error": "Ошибка при сбросе настойчивости!", + "dd_reset_success": "Успех восстановления настойчивости!", "force_close": "Принудительное закрытие", "force_close_desc": "Если Moonlight жалуется на запущенное приложение, принудительное закрытие приложения должно помочь.", "force_close_error": "Ошибка при закрытии приложения", diff --git a/src_assets/common/assets/web/public/assets/locale/sv.json b/src_assets/common/assets/web/public/assets/locale/sv.json index 04220a2e..2aebc914 100644 --- a/src_assets/common/assets/web/public/assets/locale/sv.json +++ b/src_assets/common/assets/web/public/assets/locale/sv.json @@ -7,11 +7,13 @@ "cancel": "Avbryt", "disabled": "Inaktiverad", "disabled_def": "Inaktiverad (standard)", + "disabled_def_cbox": "Standard: avmarkerad", "dismiss": "Avfärda", "do_cmd": "Kör kommando", "elevated": "Förhöjd", "enabled": "Aktiverad", "enabled_def": "Aktiverad (standard)", + "enabled_def_cbox": "Standard: markerad", "error": "Fel!", "note": "Notera:", "password": "Lösenord", @@ -150,6 +152,30 @@ "controller_desc": "Tillåter gästerna att styra värdsystemet med en gamepad / controller", "credentials_file": "Filen för inloggningsuppgifter", "credentials_file_desc": "Lagra användarnamn/lösenord separat från Sunshines statusfil.", + "dd_config_ensure_active": "Aktivera skärmen automatiskt", + "dd_config_ensure_only_display": "Inaktivera andra skärmar och aktivera endast den angivna skärmen", + "dd_config_ensure_primary": "Aktivera skärmen automatiskt och gör den till en primär display", + "dd_config_label": "Enhetens konfiguration", + "dd_config_revert_delay": "Konfigurationen återställ fördröjning", + "dd_config_revert_delay_desc": "Ytterligare fördröjning i millisekunder för att vänta innan konfiguration återställs när appen har stängts eller den senaste sessionen avslutats. Huvudsyftet är att ge en smidigare övergång när du snabbt växlar mellan appar.", + "dd_config_verify_only": "Kontrollera att skärmen är aktiverad (standard)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "Slå på/av HDR-läget som begärts av klienten (standard)", + "dd_hdr_option_disabled": "Ändra inte HDR-inställningar", + "dd_options_header": "Avancerade alternativ för visningsenhet", + "dd_refresh_rate_option": "Uppdatera hastighet", + "dd_refresh_rate_option_auto": "Använd FPS värde som tillhandahålls av klienten (standard)", + "dd_refresh_rate_option_disabled": "Ändra inte uppdateringshastighet", + "dd_refresh_rate_option_manual": "Använd manuellt inmatad uppdateringsfrekvens", + "dd_refresh_rate_option_manual_desc": "Ange uppdateringshastighet som ska användas", + "dd_resolution_option": "Upplösning", + "dd_resolution_option_auto": "Använda upplösning som tillhandahålls av klienten (standard)", + "dd_resolution_option_disabled": "Ändra inte upplösning", + "dd_resolution_option_manual": "Använd manuellt inmatad upplösning", + "dd_resolution_option_manual_desc": "Ange upplösning som ska användas", + "dd_resolution_option_ogs_desc": "Alternativet \"Optimera spelinställningar\" måste vara aktiverat på Moonlight klienten för att detta ska fungera.", + "dd_wa_hdr_toggle_desc": "När du använder en virtuell visningsenhet som för strömning kan den visa felaktig HDR-färg. Med det här alternativet aktiverat, kommer Sunshine att försöka mildra problemet.", + "dd_wa_hdr_toggle": "Aktivera högkontrastlösning för HDR", "ds4_back_as_touchpad_click": "Karta bakåt/välj att Touchpad Klicka", "ds4_back_as_touchpad_click_desc": "När DS4-emulering tvingas, kartlägg Tillbaka/välj att Touchpad Klicka", "encoder": "Tvinga en specifik kodare", @@ -168,6 +194,7 @@ "gamepad_auto": "Automatiska val", "gamepad_desc": "Välj vilken typ av gamepad som ska emuleras på värden", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "DS4 val alternativ", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Manuella DS4-alternativ", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Kontrollera hur snabba tangenter kommer att upprepa sig. Den initiala fördröjningen i millisekunder innan du upprepar nycklar.", "key_repeat_frequency": "Nyckel Upprepa Frekvens", "key_repeat_frequency_desc": "Hur ofta nycklar upprepa varje sekund. Detta konfigurerbara alternativ stöder decimaler.", - "key_rightalt_to_key_windows": "Bind höger Alt tangent till Windows-tangenten", + "key_rightalt_to_key_win": "Karta Höger Alt nyckel till Windows-tangenten", "key_rightalt_to_key_win_desc": "Det kan vara möjligt att du inte kan skicka Windows-tangenten från Moonlight direkt. I dessa fall kan det vara användbart att göra Sunshine tror att rätt Alt nyckel är Windows-tangenten", "keyboard": "Aktivera tangentbordsinmatning", "keyboard_desc": "Tillåter gäster att styra värdsystemet med tangentbordet", @@ -376,6 +403,10 @@ "third_party_notice": "Meddelande från tredje part" }, "troubleshooting": { + "dd_reset": "Återställ ihållande visningsenhetsinställningar", + "dd_reset_desc": "Om Sunshine har fastnat försöker återställa de ändrade inställningarna för visningsenheten, kan du återställa inställningarna och fortsätta att återställa visningsläget manuellt.", + "dd_reset_error": "Fel vid återställning av uthållighet!", + "dd_reset_success": "Lyckad återställning av uthållighet!", "force_close": "Tvinga stängning", "force_close_desc": "Om Moonlight klagar på att en app för närvarande är igång måste appen stängas åtgärdas.", "force_close_error": "Fel vid stängning av program", diff --git a/src_assets/common/assets/web/public/assets/locale/tr.json b/src_assets/common/assets/web/public/assets/locale/tr.json index 7621d9e9..d4c622fb 100644 --- a/src_assets/common/assets/web/public/assets/locale/tr.json +++ b/src_assets/common/assets/web/public/assets/locale/tr.json @@ -7,11 +7,13 @@ "cancel": "İptal", "disabled": "Devre Dışı", "disabled_def": "Devre Dışı (varsayılan)", + "disabled_def_cbox": "Varsayılan: işaretlenmemiş", "dismiss": "Yoksay", "do_cmd": "Komut Yap", "elevated": "Yükseltilmiş", "enabled": "Etkin", "enabled_def": "Etkin (varsayılan)", + "enabled_def_cbox": "Varsayılan: işaretli", "error": "Hata!", "note": "Not:", "password": "Şifre", @@ -150,6 +152,30 @@ "controller_desc": "Konukların ana sistemi bir gamepad / kontrol cihazı ile kontrol etmesine olanak tanır", "credentials_file": "Kimlik Bilgileri Dosyası", "credentials_file_desc": "Kullanıcı Adı/Şifre'yi Sunshine'ın durum dosyasından ayrı olarak saklayın.", + "dd_config_ensure_active": "Ekranı otomatik olarak etkinleştirin", + "dd_config_ensure_only_display": "Diğer ekranları devre dışı bırakma ve yalnızca belirtilen ekranı etkinleştirme", + "dd_config_ensure_primary": "Ekranı otomatik olarak etkinleştirin ve birincil ekran haline getirin", + "dd_config_label": "Cihaz yapılandırması", + "dd_config_revert_delay": "Yapılandırma geri dönüş gecikmesi", + "dd_config_revert_delay_desc": "Uygulama kapatıldığında veya son oturum sonlandırıldığında yapılandırmaya geri dönmeden önce beklemek için milisaniye cinsinden ek gecikme. Ana amaç, uygulamalar arasında hızlı geçiş yaparken daha yumuşak bir geçiş sağlamaktır.", + "dd_config_verify_only": "Ekranın etkin olduğunu doğrulayın (varsayılan)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "İstemci tarafından talep edildiği şekilde HDR modunu açma/kapatma (varsayılan)", + "dd_hdr_option_disabled": "HDR ayarlarını değiştirmeyin", + "dd_options_header": "Gelişmiş görüntüleme cihazı seçenekleri", + "dd_refresh_rate_option": "Yenileme hızı", + "dd_refresh_rate_option_auto": "İstemci tarafından sağlanan FPS değerini kullan (varsayılan)", + "dd_refresh_rate_option_disabled": "Yenileme hızını değiştirmeyin", + "dd_refresh_rate_option_manual": "Manuel olarak girilen yenileme hızını kullanın", + "dd_refresh_rate_option_manual_desc": "Kullanılacak yenileme hızını girin", + "dd_resolution_option": "Çözünürlük", + "dd_resolution_option_auto": "İstemci tarafından sağlanan çözünürlüğü kullan (varsayılan)", + "dd_resolution_option_disabled": "Çözünürlüğü değiştirmeyin", + "dd_resolution_option_manual": "Manuel olarak girilen çözünürlüğü kullanın", + "dd_resolution_option_manual_desc": "Kullanılacak çözünürlüğü girin", + "dd_resolution_option_ogs_desc": "Bunun çalışması için Moonlight istemcisinde \"Oyun ayarlarını optimize et\" seçeneği etkinleştirilmelidir.", + "dd_wa_hdr_toggle_desc": "Akış için sanal görüntüleme cihazı kullanırken, yanlış HDR rengi görüntüleyebilir. Bu seçenek etkinleştirildiğinde, Sunshine bu sorunu hafifletmeye çalışacaktır.", + "dd_wa_hdr_toggle": "HDR için yüksek kontrastlı geçici çözümü etkinleştirin", "ds4_back_as_touchpad_click": "Geri/Seçimi Dokunmatik Yüzeye Eşle Tıklama", "ds4_back_as_touchpad_click_desc": "DS4 emülasyonunu zorlarken, Geri/Seç'i Dokunmatik Yüzey Tıklaması ile eşleyin", "encoder": "Belirli Bir Kodlayıcıyı Zorla", @@ -168,6 +194,7 @@ "gamepad_auto": "Otomatik seçim seçenekleri", "gamepad_desc": "Ana bilgisayarda hangi tür gamepad'in taklit edileceğini seçin", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "DS4 seçim seçenekleri", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Manuel DS4 seçenekleri", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Tuşların kendilerini ne kadar hızlı tekrarlayacaklarını kontrol edin. Tuşları tekrarlamadan önce milisaniye cinsinden ilk gecikme.", "key_repeat_frequency": "Anahtar Tekrarlama Sıklığı", "key_repeat_frequency_desc": "Tuşların her saniye ne sıklıkta tekrarlanacağı. Bu yapılandırılabilir seçenek ondalık sayıları destekler.", - "key_rightalt_to_key_windows": "Sağ Alt tuşunu Windows tuşuyla eşleştirme", + "key_rightalt_to_key_win": "Sağ Alt tuşunu Windows tuşuyla eşleştirme", "key_rightalt_to_key_win_desc": "Windows Tuşunu Moonlight'tan doğrudan gönderemiyor olabilirsiniz. Bu gibi durumlarda Sunshine'ın Sağ Alt tuşunun Windows tuşu olduğunu düşünmesini sağlamak yararlı olabilir", "keyboard": "Klavye Girişini Etkinleştir", "keyboard_desc": "Konukların ana sistemi klavye ile kontrol etmesini sağlar", @@ -376,6 +403,10 @@ "third_party_notice": "Üçüncü Taraf Bildirimi" }, "troubleshooting": { + "dd_reset": "Kalıcı Ekran Aygıtı Ayarlarını Sıfırla", + "dd_reset_desc": "Sunshine değiştirilen görüntüleme cihazı ayarlarını geri yüklemeye çalışırken takılırsa, ayarları sıfırlayabilir ve ekran durumunu manuel olarak geri yüklemeye devam edebilirsiniz.", + "dd_reset_error": "Kalıcılık sıfırlanırken hata oluştu!", + "dd_reset_success": "Kalıcılığı sıfırlayan başarı!", "force_close": "Kapatmaya Zorla", "force_close_desc": "Moonlight çalışmakta olan bir uygulama hakkında şikayet ederse, uygulamayı kapatmaya zorlamak sorunu çözecektir.", "force_close_error": "Uygulama kapatılırken hata oluştu", diff --git a/src_assets/common/assets/web/public/assets/locale/uk.json b/src_assets/common/assets/web/public/assets/locale/uk.json index 033caf83..270e0d45 100644 --- a/src_assets/common/assets/web/public/assets/locale/uk.json +++ b/src_assets/common/assets/web/public/assets/locale/uk.json @@ -7,11 +7,13 @@ "cancel": "Скасувати", "disabled": "Вимкнено", "disabled_def": "Вимкнено (за замовчуванням)", + "disabled_def_cbox": "За замовчуванням: відмічено", "dismiss": "Відхилити", "do_cmd": "Виконати команду", "elevated": "Потребуються", "enabled": "Увімкнено", "enabled_def": "Увімкнено (за замовчуванням)", + "enabled_def_cbox": "За замовчуванням: вибрано", "error": "Помилка!", "note": "Примітка:", "password": "Пароль", @@ -150,6 +152,30 @@ "controller_desc": "Дозволяє гостям керувати хост-системою за допомогою геймпада / контролера", "credentials_file": "Файл облікових даних", "credentials_file_desc": "Зберігайте ім'я користувача/пароль окремо від файлу стану Sunshine.", + "dd_config_ensure_active": "Активувати автовідтворення дисплея", + "dd_config_ensure_only_display": "Вимкнути інші дисплеї та активувати тільки зазначений дисплей", + "dd_config_ensure_primary": "Автоматично активувати дисплей та зробити його основним екраном", + "dd_config_label": "Конфігурація пристрою", + "dd_config_revert_delay": "Затримка відновлення конфігурації", + "dd_config_revert_delay_desc": "Додаткова затримка в мілісекундах до очікування перед тим, як буде скасовано конфігурацію при закритті програми або припиненні останньої сесії. Головна мета - забезпечити більш плавне перемикання при швидкому перемиканні між додатками.", + "dd_config_verify_only": "Переконайтеся, що дисплей увімкнений (за замовчуванням)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "Увімкнути / вимкнути режим HDR як запит клієнтом (за замовчуванням)", + "dd_hdr_option_disabled": "Не змінювати налаштування HDR", + "dd_options_header": "Додаткові налаштування пристрою", + "dd_refresh_rate_option": "Оновити курс", + "dd_refresh_rate_option_auto": "Використовувати значення FPS наданих клієнтом (за замовчуванням)", + "dd_refresh_rate_option_disabled": "Не змінюйте частоту оновлення", + "dd_refresh_rate_option_manual": "Використовувати введений вручну курс оновлення", + "dd_refresh_rate_option_manual_desc": "Введіть швидкість оновлення, яку слід використовувати", + "dd_resolution_option": "Роздільна здатність", + "dd_resolution_option_auto": "Використовувати роздільну здатність, що надається клієнтом (за замовчуванням)", + "dd_resolution_option_disabled": "Не змінювати роздільну здатність", + "dd_resolution_option_manual": "Використовувати введену вручну", + "dd_resolution_option_manual_desc": "Введіть роздільну здатність для використання", + "dd_resolution_option_ogs_desc": "Опція \"Оптимізація налаштувань гри\" повинна бути увімкнена на віддаленому клієнті, щоб це спрацювало.", + "dd_wa_hdr_toggle_desc": "При використанні віртуального відображення пристрою для стрімінгу він може відображати неправильний HDR колір. Якщо цей параметр увімкнуто, сонячний екран намагатиметься пом'якшити цю проблему.", + "dd_wa_hdr_toggle": "Увімкнути висококонтрастний режим для HDR", "ds4_back_as_touchpad_click": "Призначити клавіші Back/Select на сенсорну клавіатуру", "ds4_back_as_touchpad_click_desc": "При включеній примусовій емуляції DS4, налаштуйте Back/Select на клацання touchpad'а", "encoder": "Примусове використання певного кодера", @@ -168,6 +194,7 @@ "gamepad_auto": "Параметри автоматичного вибору", "gamepad_desc": "Виберіть тип геймпаду для емуляції на хості", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "Опції DS4", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "Налаштування DS4 вручну", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "Керування швидкістю повторення клавіш. Початкова затримка у мілісекундах перед повторенням натискання.", "key_repeat_frequency": "Частота повторення клавіш", "key_repeat_frequency_desc": "Як часто клавіші повторюються щосекунди. Цей параметр підтримує десяткові числа.", - "key_rightalt_to_key_windows": "Зіставити праву клавішу Alt з клавішею Windows", + "key_rightalt_to_key_win": "Клавіша Alt Map праворуч від ключа Windows", "key_rightalt_to_key_win_desc": "Може статися так, що ви не зможете надіслати команду клавіші Windows з Moonshine напряму. У таких випадках може бути корисним змусити Sunshine вважати клавішу Alt праворуч клавішею Windows", "keyboard": "Увімкнути введення з клавіатури", "keyboard_desc": "Дозволити гостям керувати хост-системою за допомогою клавіатури", @@ -376,6 +403,10 @@ "third_party_notice": "Сповіщення третім особам" }, "troubleshooting": { + "dd_reset": "Скинути налаштування постійного відображення пристроїв", + "dd_reset_desc": "Якщо сонячне світло застрягло, намагається відновити змінені налаштування пристроїв для дисплея, ви можете скинути налаштування і перейти до відновлення стану екрана вручну.", + "dd_reset_error": "Помилка під час відновлення наполегливості!", + "dd_reset_success": "Успіх відновлення збереження!", "force_close": "Закрити примусово", "force_close_desc": "Якщо Moonlight скаржиться на запущену програму, примусове закриття програми має вирішити проблему.", "force_close_error": "Помилка під час закриття програми", diff --git a/src_assets/common/assets/web/public/assets/locale/zh.json b/src_assets/common/assets/web/public/assets/locale/zh.json index 9cb5bc8d..d1272ca4 100644 --- a/src_assets/common/assets/web/public/assets/locale/zh.json +++ b/src_assets/common/assets/web/public/assets/locale/zh.json @@ -7,11 +7,13 @@ "cancel": "取消", "disabled": "禁用", "disabled_def": "禁用(默认)", + "disabled_def_cbox": "默认值:未选", "dismiss": "关闭", "do_cmd": "打开时执行命令", "elevated": "提权运行", "enabled": "启用", "enabled_def": "启用(默认)", + "enabled_def_cbox": "默认值:已检查", "error": "错误!", "note": "注:", "password": "密码", @@ -91,7 +93,7 @@ "address_family_both": "IPv4+IPv6", "address_family_desc": "设置 Sunshine 使用的 IP 地址族", "address_family_ipv4": "仅 IPv4", - "always_send_scancodes": "总是发送键盘扫描码", + "always_send_scancodes": "总是发送扫描码", "always_send_scancodes_desc": "发送扫描码增强了与游戏和应用的兼容性,但可能会导致某些客户端输入不正确的键盘,而这些客户端不使用美国的英文键盘布局。 如果键盘输入在某些应用程序中根本无法工作,请启用。 如果客户端上的密钥在主机上生成错误的输入,请禁用。", "amd_coder": "AMF 编码器 (H264)", "amd_coder_desc": "允许您选择用于优先质量或编码速度的缠绕编码。 H.264。", @@ -150,6 +152,30 @@ "controller_desc": "允许客户端使用游戏手柄控制主机系统", "credentials_file": "凭据文件", "credentials_file_desc": "将用户名/密码与 Sunshine 的状态文件分开保存。", + "dd_config_ensure_active": "自动激活显示", + "dd_config_ensure_only_display": "停用其他显示器并仅激活指定的显示", + "dd_config_ensure_primary": "自动激活显示并将其作为主要显示", + "dd_config_label": "设备配置", + "dd_config_revert_delay": "配置恢复延迟", + "dd_config_revert_delay_desc": "在恢复配置前等待更多的以毫秒为单位的延迟,当应用程序已关闭或上次会话终止。 主要目的是在应用程序之间快速切换时提供更顺利的转换。", + "dd_config_verify_only": "验证显示是否已启用 (默认)", + "dd_hdr_option": "HDR", + "dd_hdr_option_auto": "按客户端请求开启/关闭HDR 模式 (默认)", + "dd_hdr_option_disabled": "不要更改 HDR 设置", + "dd_options_header": "高级显示设备选项", + "dd_refresh_rate_option": "刷新率", + "dd_refresh_rate_option_auto": "使用客户端提供的 FPS 值 (默认)", + "dd_refresh_rate_option_disabled": "不要改变刷新率", + "dd_refresh_rate_option_manual": "使用手动输入的刷新率", + "dd_refresh_rate_option_manual_desc": "输入要使用的刷新率", + "dd_resolution_option": "决 议", + "dd_resolution_option_auto": "使用客户端提供的分辨率(默认)", + "dd_resolution_option_disabled": "不改变分辨率", + "dd_resolution_option_manual": "使用手动输入的分辨率", + "dd_resolution_option_manual_desc": "输入要使用的分辨率", + "dd_resolution_option_ogs_desc": "“优化游戏设置”选项必须在月亮客户端启用才能正常工作。", + "dd_wa_hdr_toggle_desc": "当使用虚拟显示设备作为串流时,它可能会显示不正确的 HDR 颜色。启用此选项,Sunshine 将尝试缓解这个问题。", + "dd_wa_hdr_toggle": "为 HDR 启用高对比度", "ds4_back_as_touchpad_click": "映射回/选择触摸板点击", "ds4_back_as_touchpad_click_desc": "强制使用 DS4 模拟时,将“返回”/“选择”映射到触摸板点击", "encoder": "强制指定编码器", @@ -166,13 +192,14 @@ "file_state_desc": "Sunshine 保存当前状态的文件", "gamepad": "模拟游戏手柄类型", "gamepad_auto": "自动选择选项", - "gamepad_desc": "选择要在主机上模拟的游戏手表类型", + "gamepad_desc": "选择要在主机上模拟的游戏手柄类型", "gamepad_ds4": "DS4 (PS4)", + "gamepad_ds4_manual": "DS4选择选项", "gamepad_ds5": "DS5 (PS5)", "gamepad_switch": "Nintendo Pro (Switch)", "gamepad_manual": "DS4 手柄手动配置选项", "gamepad_x360": "X360 (Xbox 360)", - "gamepad_xone": "Xone (Xbox O1)", + "gamepad_xone": "XOne (Xbox One)", "global_prep_cmd": "命令准备工作", "global_prep_cmd_desc": "任何应用运行前/后要运行的命令列表。如果任何前置命令失败,应用的启动过程将被中止。", "hevc_mode": "HEVC 支持", @@ -189,7 +216,7 @@ "key_repeat_delay_desc": "控制按键重复的速度。重复按键前的初始延迟(毫秒)。", "key_repeat_frequency": "按键重复频率", "key_repeat_frequency_desc": "按键每秒重复的次数。此配置选项支持小数。", - "key_rightalt_to_key_windows": "将右 Alt 键映射到 Windows 键", + "key_rightalt_to_key_win": "将右Alt 键映射到 Windows 键", "key_rightalt_to_key_win_desc": "您可能无法直接从 Moonlight 发送 Windows 键。在这种情况下,让 Sunshine 认为右 Alt 键是 Windows 键可能会很有用。", "keyboard": "启用键盘输入", "keyboard_desc": "允许客户端使用键盘控制主机系统", @@ -263,7 +290,7 @@ "port_alert_1": "Sunshine 不能使用低于1024 的端口! ", "port_alert_2": "超过 65535 的端口不可用!", "port_desc": "设置 Sunshine 使用的端口族", - "port_http_port_note": "使用此端口连接 Moonlight。", + "port_http_port_note": "使用此端口连接 Moonlight 。", "port_note": "说明", "port_port": "端口", "port_protocol": "协议", @@ -288,7 +315,7 @@ "sunshine_name": "Sunshine 主机名称", "sunshine_name_desc": "在 Moonlight 中显示的名称。如果未指定,则使用 PC 的主机名", "sw_preset": "软件编码预设", - "sw_preset_desc": "在编码速度和压缩效率之间权衡。默认为 superfast - 超快。", + "sw_preset_desc": "在 编码速度(每秒编码的帧数) 和 压缩效率(比特流中每个比特的质量) 之间权衡。默认为 superfast - 超快。", "sw_preset_fast": "fast - 快", "sw_preset_faster": "faster - 更快", "sw_preset_medium": "medium - 中等", @@ -303,7 +330,7 @@ "sw_tune_desc": "调校选项,在预设后应用。默认值为 zerolatency。", "sw_tune_fastdecode": "fastdecode -- 通过禁用某些过滤器来加快解码速度", "sw_tune_film": "film -- 用于高质量的电影内容;降低去块", - "sw_tune_grain": "grain -- 保留旧的颗粒胶片材料的颗粒结构", + "sw_tune_grain": "谷物——保存老旧的谷物结构,灰色胶卷材料", "sw_tune_stillimage": "stillimage -- 适用于类似幻灯片的内容", "sw_tune_zerolatency": "zerolatency -- 适用于快速编码和低延迟串流(默认值)", "touchpad_as_ds4": "如果客户端报告游戏手柄存在触摸板,则模拟一个 DS4 游戏手柄", @@ -342,7 +369,7 @@ "configuration": "配置", "home": "首页", "password": "更改密码", - "pin": "置顶", + "pin": "Pin 码", "theme_auto": "自动操作", "theme_dark": "深色", "theme_light": "浅色", @@ -376,6 +403,10 @@ "third_party_notice": "第三方通知" }, "troubleshooting": { + "dd_reset": "重置持久显示设备设置", + "dd_reset_desc": "如果Sunshine 被卡住试图恢复更改的显示设备设置,您可以重置设置并手动恢复显示状态。", + "dd_reset_error": "重置持久性时发生错误!", + "dd_reset_success": "成功重置持久性!", "force_close": "强制结束运行", "force_close_desc": "如果 Moonlight 抱怨某个应用正在运行,强制关闭该应用应该可以解决问题。", "force_close_error": "关闭应用时出错", From 6a233cbcbfe1475d88bbedd03b848df205f2b268 Mon Sep 17 00:00:00 2001 From: Lukas Senionis Date: Thu, 9 Jan 2025 01:44:11 +0200 Subject: [PATCH 11/20] feat(ddprobe): allow to manually specify gpu preference (#3521) --- docs/configuration.md | 31 ++++++++++++++++++ src/config.cpp | 2 ++ src/config.h | 1 + src/platform/windows/display_base.cpp | 32 +++++++++++-------- src_assets/common/assets/web/config.html | 1 + .../assets/web/configs/tabs/AudioVideo.vue | 12 +++++++ .../tabs/audiovideo/DisplayDeviceOptions.vue | 2 +- .../assets/web/public/assets/locale/en.json | 2 ++ 8 files changed, 68 insertions(+), 15 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 78dacddd..57bb6d9f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -848,6 +848,37 @@ editing the `conf` file in a text editor. Use the examples as reference.
ChoicesChoices bg Bulgarian
ja Japanese
koKorean
pl Polish
+### gpu_preference + + + + + + + + + + + + + + +
Description + Specify the GPU preference for the Sunshine process. +
+
+ If set to negative number (-1 by default), Sunshine will try to detect the best GPU for the streamed display, but if it fails you will get a black screen. +
+ Setting it to 0 will allow Windows to try and select the best GPU. +
+ Setting it to 1 and above will prioritize the GPU that matches this number (the number has to be guessed, but it starts at 1 and increases). + @note{Applies to Windows only.} +
Default@code{} + -1 + @endcode
Example@code{} + 2 + @endcode
+ ### output_name diff --git a/src/config.cpp b/src/config.cpp index cb3337d5..cf2b90a2 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -436,6 +436,7 @@ namespace config { {}, // capture {}, // encoder {}, // adapter_name + -1, // gpu_preference {}, // output_name { @@ -1088,6 +1089,7 @@ namespace config { string_f(vars, "capture", video.capture); string_f(vars, "encoder", video.encoder); string_f(vars, "adapter_name", video.adapter_name); + int_f(vars, "gpu_preference", video.gpu_preference); string_f(vars, "output_name", video.output_name); generic_f(vars, "dd_configuration_option", video.dd.configuration_option, dd::config_option_from_view); diff --git a/src/config.h b/src/config.h index e481a1e7..7e1813e6 100644 --- a/src/config.h +++ b/src/config.h @@ -77,6 +77,7 @@ namespace config { std::string capture; std::string encoder; std::string adapter_name; + int gpu_preference; std::string output_name; struct dd_t { diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index 3239c68b..b52c3c3b 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -386,12 +386,7 @@ namespace platf::dxgi { // would have been raised first if it wasn't. if (result == S_OK || result == E_ACCESSDENIED) { // We found a working GPU preference, so set ourselves to use that. - if (set_gpu_preference_on_self(i)) { - return true; - } - else { - return false; - } + return set_gpu_preference_on_self(i); } } @@ -418,16 +413,25 @@ namespace platf::dxgi { return true; } - // Try probing with different GPU preferences and verify_frame_capture flag - if (validate_and_test_gpu_preference(display_name, true)) { - set_gpu_preference = true; - return true; + // If the GPU preference was manually specified, we can skip the probe. + if (config::video.gpu_preference >= 0) { + if (set_gpu_preference_on_self(config::video.gpu_preference)) { + set_gpu_preference = true; + return true; + } } + else { + // Try probing with different GPU preferences and verify_frame_capture flag + if (validate_and_test_gpu_preference(display_name, true)) { + set_gpu_preference = true; + return true; + } - // If no valid configuration was found, try again with verify_frame_capture == false - if (validate_and_test_gpu_preference(display_name, false)) { - set_gpu_preference = true; - return true; + // If no valid configuration was found, try again with verify_frame_capture == false + if (validate_and_test_gpu_preference(display_name, false)) { + set_gpu_preference = true; + return true; + } } // If neither worked, return false diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 300e1c36..3eec36d7 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -169,6 +169,7 @@ "virtual_sink": "", "install_steam_audio_drivers": "enabled", "adapter_name": "", + "gpu_preference": -1, "output_name": "", "dd_configuration_option": "verify_only", "dd_resolution_option": "auto", diff --git a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue index 531d0972..dfcc1c4e 100644 --- a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue +++ b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue @@ -69,6 +69,18 @@ const config = ref(props.config) :config="config" /> + + + + {{ $t('config.dd_config_revert_delay') }} -
{{ $t('config.dd_config_revert_delay_desc') }} diff --git a/src_assets/common/assets/web/public/assets/locale/en.json b/src_assets/common/assets/web/public/assets/locale/en.json index 968bb517..3bcfd436 100644 --- a/src_assets/common/assets/web/public/assets/locale/en.json +++ b/src_assets/common/assets/web/public/assets/locale/en.json @@ -186,6 +186,8 @@ "fec_percentage": "FEC Percentage", "fec_percentage_desc": "Percentage of error correcting packets per data packet in each video frame. Higher values can correct for more network packet loss, but at the cost of increasing bandwidth usage.", "ffmpeg_auto": "auto -- let ffmpeg decide (default)", + "gpu_preference": "GPU Preference", + "gpu_preference_desc": "Specify the GPU preference for the Sunshine process. If set to negative number (-1 by default), Sunshine will try to detect the best GPU for the streamed display, but if it fails you will get a black screen. Setting it to 0 will allow Windows to try and select the best GPU. Setting it to 1 and above will prioritize the GPU that matches this number (the number has to be guessed, but it starts at 1 and increases).", "file_apps": "Apps File", "file_apps_desc": "The file where current apps of Sunshine are stored.", "file_state": "State File", From 80ecf19d40fbb02d940e4ca73ae15a260f77a9cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 04:58:57 +0000 Subject: [PATCH 12/20] build(deps): bump vue-i18n from 9.14.0 to 11.0.1 (#3489) Bumps [vue-i18n](https://github.com/intlify/vue-i18n/tree/HEAD/packages/vue-i18n) from 9.14.0 to 11.0.1. - [Release notes](https://github.com/intlify/vue-i18n/releases) - [Changelog](https://github.com/intlify/vue-i18n/blob/master/CHANGELOG.md) - [Commits](https://github.com/intlify/vue-i18n/commits/v11.0.1/packages/vue-i18n) --- updated-dependencies: - dependency-name: vue-i18n dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0775f146..666acfcd 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "dependencies": { "@lizardbyte/shared-web": "2024.921.191855", "vue": "3.5.13", - "vue-i18n": "9.14.0" + "vue-i18n": "11.0.1" }, "devDependencies": { "@vitejs/plugin-vue": "4.6.2", From 40ac7186918a7f7a57ce0cc28e89390f38d98f7c Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Fri, 10 Jan 2025 15:12:52 -0500 Subject: [PATCH 13/20] fix(confighttp): do not return 200 on errors (#3385) Co-authored-by: Lukas Senionis <22381748+FrogTheFrog@users.noreply.github.com> --- docs/Doxyfile | 4 + docs/api.js | 130 ++++++++++++ docs/api.md | 11 +- src/confighttp.cpp | 485 +++++++++++++++++++++++++++------------------ 4 files changed, 437 insertions(+), 193 deletions(-) create mode 100644 docs/api.js diff --git a/docs/Doxyfile b/docs/Doxyfile index 7c08c4f0..6e95fd0f 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -62,4 +62,8 @@ INPUT = ../README.md \ HTML_EXTRA_STYLESHEET += doc-styles.css # extra js +HTML_EXTRA_FILES += api.js HTML_EXTRA_FILES += configuration.js + +# custom aliases +ALIASES += api_examples{3|}="@htmlonly@endhtmlonly" diff --git a/docs/api.js b/docs/api.js new file mode 100644 index 00000000..5f887e94 --- /dev/null +++ b/docs/api.js @@ -0,0 +1,130 @@ +function generateExamples(endpoint, method, body = null) { + let curlBodyString = ''; + let psBodyString = ''; + + if (body) { + const curlJsonString = JSON.stringify(body).replace(/"/g, '\\"'); + curlBodyString = ` -d "${curlJsonString}"`; + psBodyString = `-Body (ConvertTo-Json ${JSON.stringify(body)})`; + } + + return { + cURL: `curl -u user:pass -X ${method.trim()} -k https://localhost:47990${endpoint.trim()}${curlBodyString}`, + Python: `import json +import requests +from requests.auth import HTTPBasicAuth + +requests.${method.trim().toLowerCase()}( + auth=HTTPBasicAuth('user', 'pass'), + url='https://localhost:47990${endpoint.trim()}', + verify=False,${body ? `\n json=${JSON.stringify(body)},` : ''} +).json()`, + JavaScript: `fetch('https://localhost:47990${endpoint.trim()}', { + method: '${method.trim()}', + headers: { + 'Authorization': 'Basic ' + btoa('user:pass'), + 'Content-Type': 'application/json', + }${body ? `,\n body: JSON.stringify(${JSON.stringify(body)}),` : ''} +}) +.then(response => response.json()) +.then(data => console.log(data));`, + PowerShell: `Invoke-RestMethod \` + -SkipCertificateCheck \` + -Uri 'https://localhost:47990${endpoint.trim()}' \` + -Method ${method.trim()} \` + -Headers @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes('user:pass'))} + ${psBodyString}` + }; +} + +function hashString(str) { + let hash = 0; + for (let i = 0; i < str.length; i++) { + const char = str.charCodeAt(i); + hash = (hash << 5) - hash + char; + hash |= 0; // Convert to 32bit integer + } + return hash; +} + +function createTabs(examples) { + const languages = Object.keys(examples); + let tabs = '
'; + let content = '
'; + + languages.forEach((lang, index) => { + const hash = hashString(examples[lang]); + tabs += ``; + content += `
+
+
+ ${examples[lang].split('\n').map(line => `
${line}
`).join('')} +
+ + + + + + +
+
`; + }); + + tabs += '
'; + content += '
'; + + setTimeout(() => { + languages.forEach((lang, index) => { + const hash = hashString(examples[lang]); + const copyButton = document.getElementById(`copy-button-${lang}-${hash}`); + copyButton.addEventListener('click', copyContent); + }); + }, 0); + + return tabs + content; +} + +function copyContent() { + const content = this.previousElementSibling.cloneNode(true); + if (content instanceof Element) { + // filter out line number from file listings + content.querySelectorAll(".lineno, .ttc").forEach((node) => { + node.remove(); + }); + let textContent = Array.from(content.querySelectorAll('.line')) + .map(line => line.innerText) + .join('\n') + .trim(); // Join lines with newline characters and trim leading/trailing whitespace + navigator.clipboard.writeText(textContent); + this.classList.add("success"); + this.innerHTML = ``; + window.setTimeout(() => { + this.classList.remove("success"); + this.innerHTML = ``; + }, 980); + } else { + console.error('Failed to copy: content is not a DOM element'); + } +} + +function openTab(evt, lang) { + const tabcontent = document.getElementsByClassName("tabcontent"); + for (const content of tabcontent) { + content.style.display = "none"; + } + + const tablinks = document.getElementsByClassName("tab-button"); + for (const link of tablinks) { + link.className = link.className.replace(" active", ""); + } + + const selectedTabs = document.querySelectorAll(`#${lang}`); + for (const tab of selectedTabs) { + tab.style.display = "block"; + } + + const selectedButtons = document.querySelectorAll(`.tab-button[onclick*="${lang}"]`); + for (const button of selectedButtons) { + button.className += " active"; + } +} diff --git a/docs/api.md b/docs/api.md index 2c6e6409..e93f500c 100644 --- a/docs/api.md +++ b/docs/api.md @@ -5,6 +5,10 @@ Sunshine has a RESTful API which can be used to interact with the service. Unless otherwise specified, authentication is required for all API calls. You can authenticate using basic authentication with the admin username and password. +@htmlonly + +@endhtmlonly + ## GET /api/apps @copydoc confighttp::getApps() @@ -14,7 +18,7 @@ basic authentication with the admin username and password. ## POST /api/apps @copydoc confighttp::saveApp() -## DELETE /api/apps{index} +## DELETE /api/apps/{index} @copydoc confighttp::deleteApp() ## POST /api/covers/upload @@ -32,6 +36,9 @@ basic authentication with the admin username and password. ## POST /api/restart @copydoc confighttp::restart() +## POST /api/reset-display-device-persistence +@copydoc confighttp::resetDisplayDevicePersistence() + ## POST /api/password @copydoc confighttp::savePassword() @@ -47,7 +54,7 @@ basic authentication with the admin username and password. ## GET /api/clients/list @copydoc confighttp::listClients() -## GET /api/apps/close +## POST /api/apps/close @copydoc confighttp::closeApp()
diff --git a/src/confighttp.cpp b/src/confighttp.cpp index 9b8570e0..f2a36441 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -58,6 +58,10 @@ namespace confighttp { REMOVE ///< Remove client }; + /** + * @brief Log the request details. + * @param request The HTTP request object. + */ void print_req(const req_https_t &request) { BOOST_LOG(debug) << "METHOD :: "sv << request->method; @@ -76,6 +80,23 @@ namespace confighttp { BOOST_LOG(debug) << " [--] "sv; } + /** + * @brief Send a response. + * @param response The HTTP response object. + * @param output_tree The JSON tree to send. + */ + void + send_response(resp_https_t response, const pt::ptree &output_tree) { + std::ostringstream data; + pt::write_json(data, output_tree); + response->write(data.str()); + } + + /** + * @brief Send a 401 Unauthorized response. + * @param response The HTTP response object. + * @param request The HTTP request object. + */ void send_unauthorized(resp_https_t response, req_https_t request) { auto address = net::addr_to_normalized_string(request->remote_endpoint().address()); @@ -86,6 +107,12 @@ namespace confighttp { response->write(SimpleWeb::StatusCode::client_error_unauthorized, headers); } + /** + * @brief Send a redirect response. + * @param response The HTTP response object. + * @param request The HTTP request object. + * @param path The path to redirect to. + */ void send_redirect(resp_https_t response, req_https_t request, const char *path) { auto address = net::addr_to_normalized_string(request->remote_endpoint().address()); @@ -96,6 +123,12 @@ namespace confighttp { response->write(SimpleWeb::StatusCode::redirection_temporary_redirect, headers); } + /** + * @brief Authenticate the user. + * @param response The HTTP response object. + * @param request The HTTP request object. + * @return True if the user is authenticated, false otherwise. + */ bool authenticate(resp_https_t response, req_https_t request) { auto address = net::addr_to_normalized_string(request->remote_endpoint().address()); @@ -142,21 +175,56 @@ namespace confighttp { return true; } + /** + * @brief Send a 404 Not Found response. + * @param response The HTTP response object. + * @param request The HTTP request object. + */ void - not_found(resp_https_t response, req_https_t request) { + not_found(resp_https_t response, [[maybe_unused]] req_https_t request) { + constexpr SimpleWeb::StatusCode code = SimpleWeb::StatusCode::client_error_not_found; + pt::ptree tree; - tree.put("root..status_code", 404); + tree.put("status_code", static_cast(code)); + tree.put("error", "Not Found"); std::ostringstream data; + pt::write_json(data, tree); - pt::write_xml(data, tree); - response->write(data.str()); + SimpleWeb::CaseInsensitiveMultimap headers; + headers.emplace("Content-Type", "application/json"); - *response << "HTTP/1.1 404 NOT FOUND\r\n" - << data.str(); + response->write(code, data.str(), headers); } /** + * @brief Send a 400 Bad Request response. + * @param response The HTTP response object. + * @param request The HTTP request object. + * @param error_message The error message to include in the response. + */ + void + bad_request(resp_https_t response, [[maybe_unused]] req_https_t request, const std::string &error_message = "Bad Request") { + constexpr SimpleWeb::StatusCode code = SimpleWeb::StatusCode::client_error_bad_request; + + pt::ptree tree; + tree.put("status_code", static_cast(code)); + tree.put("status", false); + tree.put("error", error_message); + + std::ostringstream data; + pt::write_json(data, tree); + + SimpleWeb::CaseInsensitiveMultimap headers; + headers.emplace("Content-Type", "application/json"); + + response->write(code, data.str(), headers); + } + + /** + * @brief Get the index page. + * @param response The HTTP response object. + * @param request The HTTP request object. * @todo combine these functions into a single function that accepts the page, i.e "index", "pin", "apps" */ void @@ -171,6 +239,11 @@ namespace confighttp { response->write(content, headers); } + /** + * @brief Get the PIN page. + * @param response The HTTP response object. + * @param request The HTTP request object. + */ void getPinPage(resp_https_t response, req_https_t request) { if (!authenticate(response, request)) return; @@ -183,6 +256,11 @@ namespace confighttp { response->write(content, headers); } + /** + * @brief Get the apps page. + * @param response The HTTP response object. + * @param request The HTTP request object. + */ void getAppsPage(resp_https_t response, req_https_t request) { if (!authenticate(response, request)) return; @@ -196,6 +274,11 @@ namespace confighttp { response->write(content, headers); } + /** + * @brief Get the clients page. + * @param response The HTTP response object. + * @param request The HTTP request object. + */ void getClientsPage(resp_https_t response, req_https_t request) { if (!authenticate(response, request)) return; @@ -208,6 +291,11 @@ namespace confighttp { response->write(content, headers); } + /** + * @brief Get the configuration page. + * @param response The HTTP response object. + * @param request The HTTP request object. + */ void getConfigPage(resp_https_t response, req_https_t request) { if (!authenticate(response, request)) return; @@ -220,6 +308,11 @@ namespace confighttp { response->write(content, headers); } + /** + * @brief Get the password page. + * @param response The HTTP response object. + * @param request The HTTP request object. + */ void getPasswordPage(resp_https_t response, req_https_t request) { if (!authenticate(response, request)) return; @@ -232,6 +325,11 @@ namespace confighttp { response->write(content, headers); } + /** + * @brief Get the welcome page. + * @param response The HTTP response object. + * @param request The HTTP request object. + */ void getWelcomePage(resp_https_t response, req_https_t request) { print_req(request); @@ -245,6 +343,11 @@ namespace confighttp { response->write(content, headers); } + /** + * @brief Get the troubleshooting page. + * @param response The HTTP response object. + * @param request The HTTP request object. + */ void getTroubleshootingPage(resp_https_t response, req_https_t request) { if (!authenticate(response, request)) return; @@ -258,6 +361,9 @@ namespace confighttp { } /** + * @brief Get the favicon image. + * @param response The HTTP response object. + * @param request The HTTP request object. * @todo combine function with getSunshineLogoImage and possibly getNodeModules * @todo use mime_types map */ @@ -272,6 +378,9 @@ namespace confighttp { } /** + * @brief Get the Sunshine logo image. + * @param response The HTTP response object. + * @param request The HTTP request object. * @todo combine function with getFaviconImage and possibly getNodeModules * @todo use mime_types map */ @@ -285,12 +394,23 @@ namespace confighttp { response->write(SimpleWeb::StatusCode::success_ok, in, headers); } + /** + * @brief Check if a path is a child of another path. + * @param base The base path. + * @param query The path to check. + * @return True if the path is a child of the base path, false otherwise. + */ bool isChildPath(fs::path const &base, fs::path const &query) { auto relPath = fs::relative(base, query); return *(relPath.begin()) != fs::path(".."); } + /** + * @brief Get an asset from the node_modules directory. + * @param response The HTTP response object. + * @param request The HTTP request object. + */ void getNodeModules(resp_https_t response, req_https_t request) { print_req(request); @@ -303,32 +423,37 @@ namespace confighttp { // Don't do anything if file does not exist or is outside the assets directory if (!isChildPath(filePath, nodeModulesPath)) { BOOST_LOG(warning) << "Someone requested a path " << filePath << " that is outside the assets folder"; - response->write(SimpleWeb::StatusCode::client_error_bad_request, "Bad Request"); + bad_request(response, request); + return; } - else if (!fs::exists(filePath)) { - response->write(SimpleWeb::StatusCode::client_error_not_found); + if (!fs::exists(filePath)) { + not_found(response, request); + return; } - else { - auto relPath = fs::relative(filePath, webDirPath); - // get the mime type from the file extension mime_types map - // remove the leading period from the extension - auto mimeType = mime_types.find(relPath.extension().string().substr(1)); - // check if the extension is in the map at the x position - if (mimeType != mime_types.end()) { - // if it is, set the content type to the mime type - SimpleWeb::CaseInsensitiveMultimap headers; - headers.emplace("Content-Type", mimeType->second); - std::ifstream in(filePath.string(), std::ios::binary); - response->write(SimpleWeb::StatusCode::success_ok, in, headers); - } - // do not return any file if the type is not in the map + + auto relPath = fs::relative(filePath, webDirPath); + // get the mime type from the file extension mime_types map + // remove the leading period from the extension + auto mimeType = mime_types.find(relPath.extension().string().substr(1)); + // check if the extension is in the map at the x position + if (mimeType == mime_types.end()) { + bad_request(response, request); + return; } + + // if it is, set the content type to the mime type + SimpleWeb::CaseInsensitiveMultimap headers; + headers.emplace("Content-Type", mimeType->second); + std::ifstream in(filePath.string(), std::ios::binary); + response->write(SimpleWeb::StatusCode::success_ok, in, headers); } /** * @brief Get the list of available applications. * @param response The HTTP response object. * @param request The HTTP request object. + * + * @api_examples{/api/apps| GET| null} */ void getApps(resp_https_t response, req_https_t request) { @@ -346,6 +471,8 @@ namespace confighttp { * @brief Get the logs from the log file. * @param response The HTTP response object. * @param request The HTTP request object. + * + * @api_examples{/api/logs| GET| null} */ void getLogs(resp_https_t response, req_https_t request) { @@ -360,7 +487,7 @@ namespace confighttp { } /** - * @brief Save an application. If the application already exists, it will be updated, otherwise it will be added. + * @brief 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. * @param response The HTTP response object. * @param request The HTTP request object. * The body for the post request should be JSON serialized in the following format: @@ -385,9 +512,11 @@ namespace confighttp { * "detached": [ * "Detached command" * ], - * "image-path": "Full path to the application image. Must be a png file.", + * "image-path": "Full path to the application image. Must be a png file." * } * @endcode + * + * @api_examples{/api/apps| POST| {"name":"Hello, World!","index":-1}} */ void saveApp(resp_https_t response, req_https_t request) { @@ -398,19 +527,12 @@ namespace confighttp { std::stringstream ss; ss << request->content.rdbuf(); - pt::ptree outputTree; - auto g = util::fail_guard([&]() { - std::ostringstream data; - - pt::write_json(data, outputTree); - response->write(data.str()); - }); - - pt::ptree inputTree, fileTree; - BOOST_LOG(info) << config::stream.file_apps; try { // TODO: Input Validation + pt::ptree fileTree; + pt::ptree inputTree; + pt::ptree outputTree; pt::read_json(ss, inputTree); pt::read_json(config::stream.file_apps, fileTree); @@ -464,23 +586,23 @@ namespace confighttp { fileTree.add_child("apps", sorted_apps); pt::write_json(config::stream.file_apps, fileTree); + proc::refresh(config::stream.file_apps); + + outputTree.put("status", true); + send_response(response, outputTree); } catch (std::exception &e) { BOOST_LOG(warning) << "SaveApp: "sv << e.what(); - - outputTree.put("status", "false"); - outputTree.put("error", "Invalid Input JSON"); - return; + bad_request(response, request, e.what()); } - - outputTree.put("status", "true"); - proc::refresh(config::stream.file_apps); } /** * @brief Delete an application. * @param response The HTTP response object. * @param request The HTTP request object. + * + * @api_examples{/api/apps/9999| DELETE| null} */ void deleteApp(resp_https_t response, req_https_t request) { @@ -489,46 +611,46 @@ namespace confighttp { print_req(request); pt::ptree outputTree; - auto g = util::fail_guard([&]() { - std::ostringstream data; - - pt::write_json(data, outputTree); - response->write(data.str()); - }); - pt::ptree fileTree; try { + pt::ptree fileTree; + pt::ptree newApps; pt::read_json(config::stream.file_apps, fileTree); auto &apps_node = fileTree.get_child("apps"s); int index = stoi(request->path_match[1]); - if (index < 0) { - outputTree.put("status", "false"); - outputTree.put("error", "Invalid Index"); + if (index < 0 || index >= static_cast(apps_node.size())) { + std::string error; + if (const int max_index = static_cast(apps_node.size()) - 1; max_index < 0) { + error = "No applications to delete"; + } + else { + error = "'index' out of range, max index is "s + std::to_string(max_index); + } + bad_request(response, request, error); return; } - else { - // Unfortunately Boost PT does not allow to directly edit the array, copy should do the trick - pt::ptree newApps; - int i = 0; - for (const auto &[k, v] : apps_node) { - if (i++ != index) { - newApps.push_back(std::make_pair("", v)); - } + + // Unfortunately Boost PT does not allow to directly edit the array, copy should do the trick + int i = 0; + for (const auto &[k, v] : apps_node) { + if (i++ != index) { + newApps.push_back(std::make_pair("", v)); } - fileTree.erase("apps"); - fileTree.push_back(std::make_pair("apps", newApps)); } + fileTree.erase("apps"); + fileTree.push_back(std::make_pair("apps", newApps)); + pt::write_json(config::stream.file_apps, fileTree); + proc::refresh(config::stream.file_apps); + + outputTree.put("status", true); + outputTree.put("result", "application "s + std::to_string(index) + " deleted"); + send_response(response, outputTree); } catch (std::exception &e) { BOOST_LOG(warning) << "DeleteApp: "sv << e.what(); - outputTree.put("status", "false"); - outputTree.put("error", "Invalid File JSON"); - return; + bad_request(response, request, e.what()); } - - outputTree.put("status", "true"); - proc::refresh(config::stream.file_apps); } /** @@ -539,9 +661,11 @@ namespace confighttp { * @code{.json} * { * "key": "igdb_", - * "url": "https://images.igdb.com/igdb/image/upload/t_cover_big_2x/.png", + * "url": "https://images.igdb.com/igdb/image/upload/t_cover_big_2x/.png" * } * @endcode + * + * @api_examples{/api/covers/upload| POST| {"key":"igdb_1234","url":"https://images.igdb.com/igdb/image/upload/t_cover_big_2x/abc123.png"}} */ void uploadCover(resp_https_t response, req_https_t request) { @@ -551,31 +675,19 @@ namespace confighttp { std::stringstream configStream; ss << request->content.rdbuf(); pt::ptree outputTree; - auto g = util::fail_guard([&]() { - std::ostringstream data; - - SimpleWeb::StatusCode code = SimpleWeb::StatusCode::success_ok; - if (outputTree.get_child_optional("error").has_value()) { - code = SimpleWeb::StatusCode::client_error_bad_request; - } - - pt::write_json(data, outputTree); - response->write(code, data.str()); - }); pt::ptree inputTree; try { pt::read_json(ss, inputTree); } catch (std::exception &e) { BOOST_LOG(warning) << "UploadCover: "sv << e.what(); - outputTree.put("status", "false"); - outputTree.put("error", e.what()); + bad_request(response, request, e.what()); return; } auto key = inputTree.get("key", ""); if (key.empty()) { - outputTree.put("error", "Cover key is required"); + bad_request(response, request, "Cover key is required"); return; } auto url = inputTree.get("url", ""); @@ -586,11 +698,11 @@ namespace confighttp { std::basic_string path = coverdir + http::url_escape(key) + ".png"; if (!url.empty()) { if (http::url_get_host(url) != "images.igdb.com") { - outputTree.put("error", "Only images.igdb.com is allowed"); + bad_request(response, request, "Only images.igdb.com is allowed"); return; } if (!http::download_file(url, path)) { - outputTree.put("error", "Failed to download cover"); + bad_request(response, request, "Failed to download cover"); return; } } @@ -600,13 +712,17 @@ namespace confighttp { std::ofstream imgfile(path); imgfile.write(data.data(), (int) data.size()); } + outputTree.put("status", true); outputTree.put("path", path); + send_response(response, outputTree); } /** * @brief Get the configuration settings. * @param response The HTTP response object. * @param request The HTTP request object. + * + * @api_examples{/api/config| GET| null} */ void getConfig(resp_https_t response, req_https_t request) { @@ -615,14 +731,7 @@ namespace confighttp { print_req(request); pt::ptree outputTree; - auto g = util::fail_guard([&]() { - std::ostringstream data; - - pt::write_json(data, outputTree); - response->write(data.str()); - }); - - outputTree.put("status", "true"); + outputTree.put("status", true); outputTree.put("platform", SUNSHINE_PLATFORM); outputTree.put("version", PROJECT_VER); @@ -631,12 +740,16 @@ namespace confighttp { for (auto &[name, value] : vars) { outputTree.put(std::move(name), std::move(value)); } + + send_response(response, outputTree); } /** * @brief Get the locale setting. This endpoint does not require authentication. * @param response The HTTP response object. * @param request The HTTP request object. + * + * @api_examples{/api/configLocale| GET| null} */ void getLocale(resp_https_t response, req_https_t request) { @@ -645,15 +758,9 @@ namespace confighttp { print_req(request); pt::ptree outputTree; - auto g = util::fail_guard([&]() { - std::ostringstream data; - - pt::write_json(data, outputTree); - response->write(data.str()); - }); - - outputTree.put("status", "true"); + outputTree.put("status", true); outputTree.put("locale", config::sunshine.locale); + send_response(response, outputTree); } /** @@ -668,6 +775,8 @@ namespace confighttp { * @endcode * * @attention{It is recommended to ONLY save the config settings that differ from the default behavior.} + * + * @api_examples{/api/config| POST| {"key":"value"}} */ void saveConfig(resp_https_t response, req_https_t request) { @@ -678,16 +787,10 @@ namespace confighttp { std::stringstream ss; std::stringstream configStream; ss << request->content.rdbuf(); - pt::ptree outputTree; - auto g = util::fail_guard([&]() { - std::ostringstream data; - - pt::write_json(data, outputTree); - response->write(data.str()); - }); - pt::ptree inputTree; try { // TODO: Input Validation + pt::ptree inputTree; + pt::ptree outputTree; pt::read_json(ss, inputTree); for (const auto &[k, v] : inputTree) { std::string value = inputTree.get(k); @@ -696,12 +799,12 @@ namespace confighttp { configStream << k << " = " << value << std::endl; } file_handler::write_file(config::sunshine.config_file.c_str(), configStream.str()); + outputTree.put("status", true); + send_response(response, outputTree); } catch (std::exception &e) { BOOST_LOG(warning) << "SaveConfig: "sv << e.what(); - outputTree.put("status", "false"); - outputTree.put("error", e.what()); - return; + bad_request(response, request, e.what()); } } @@ -709,6 +812,8 @@ namespace confighttp { * @brief Restart Sunshine. * @param response The HTTP response object. * @param request The HTTP request object. + * + * @api_examples{/api/restart| POST| null} */ void restart(resp_https_t response, req_https_t request) { @@ -720,6 +825,24 @@ namespace confighttp { platf::restart(); } + /** + * @brief Reset the display device persistence. + * @param response The HTTP response object. + * @param request The HTTP request object. + * + * @api_examples{/api/reset-display-device-persistence| POST| null} + */ + void + resetDisplayDevicePersistence(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) return; + + print_req(request); + + pt::ptree outputTree; + outputTree.put("status", display_device::reset_persistence()); + send_response(response, outputTree); + } + /** * @brief Update existing credentials. * @param response The HTTP response object. @@ -734,43 +857,24 @@ namespace confighttp { * "confirmNewPassword": "Confirm New Password" * } * @endcode + * + * @api_examples{/api/password| POST| {"currentUsername":"admin","currentPassword":"admin","newUsername":"admin","newPassword":"admin","confirmNewPassword":"admin"}} */ - void - resetDisplayDevicePersistence(resp_https_t response, req_https_t request) { - if (!authenticate(response, request)) return; - - print_req(request); - - pt::ptree outputTree; - auto g = util::fail_guard([&outputTree, &response]() { - std::ostringstream data; - pt::write_json(data, outputTree); - response->write(data.str()); - }); - - outputTree.put("status", display_device::reset_persistence()); - } - void savePassword(resp_https_t response, req_https_t request) { if (!config::sunshine.username.empty() && !authenticate(response, request)) return; print_req(request); + std::vector errors = {}; std::stringstream ss; std::stringstream configStream; ss << request->content.rdbuf(); - pt::ptree inputTree, outputTree; - - auto g = util::fail_guard([&]() { - std::ostringstream data; - pt::write_json(data, outputTree); - response->write(data.str()); - }); - try { // TODO: Input Validation + pt::ptree inputTree; + pt::ptree outputTree; pt::read_json(ss, inputTree); auto username = inputTree.count("currentUsername") > 0 ? inputTree.get("currentUsername") : ""; auto newUsername = inputTree.get("newUsername"); @@ -779,15 +883,13 @@ namespace confighttp { auto confirmPassword = inputTree.count("confirmNewPassword") > 0 ? inputTree.get("confirmNewPassword") : ""; if (newUsername.length() == 0) newUsername = username; if (newUsername.length() == 0) { - outputTree.put("status", false); - outputTree.put("error", "Invalid Username"); + errors.emplace_back("Invalid Username"); } else { auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string(); if (config::sunshine.username.empty() || (boost::iequals(username, config::sunshine.username) && hash == config::sunshine.password)) { if (newPassword.empty() || newPassword != confirmPassword) { - outputTree.put("status", false); - outputTree.put("error", "Password Mismatch"); + errors.emplace_back("Password Mismatch"); } else { http::save_user_creds(config::sunshine.credentials_file, newUsername, newPassword); @@ -796,16 +898,25 @@ namespace confighttp { } } else { - outputTree.put("status", false); - outputTree.put("error", "Invalid Current Credentials"); + errors.emplace_back("Invalid Current Credentials"); } } + + if (!errors.empty()) { + // join the errors array + std::string error = std::accumulate(errors.begin(), errors.end(), std::string(), + [](const std::string &a, const std::string &b) { + return a.empty() ? b : a + ", " + b; + }); + bad_request(response, request, error); + return; + } + + send_response(response, outputTree); } catch (std::exception &e) { BOOST_LOG(warning) << "SavePassword: "sv << e.what(); - outputTree.put("status", false); - outputTree.put("error", e.what()); - return; + bad_request(response, request, e.what()); } } @@ -820,6 +931,8 @@ namespace confighttp { * "name": "Friendly Client Name" * } * @endcode + * + * @api_examples{/api/pin| POST| {"pin":"1234","name":"My PC"}} */ void savePin(resp_https_t response, req_https_t request) { @@ -830,26 +943,19 @@ namespace confighttp { std::stringstream ss; ss << request->content.rdbuf(); - pt::ptree inputTree, outputTree; - - auto g = util::fail_guard([&]() { - std::ostringstream data; - pt::write_json(data, outputTree); - response->write(data.str()); - }); - try { // TODO: Input Validation + pt::ptree inputTree; + pt::ptree outputTree; pt::read_json(ss, inputTree); std::string pin = inputTree.get("pin"); std::string name = inputTree.get("name"); outputTree.put("status", nvhttp::pin(pin, name)); + send_response(response, outputTree); } catch (std::exception &e) { BOOST_LOG(warning) << "SavePin: "sv << e.what(); - outputTree.put("status", false); - outputTree.put("error", e.what()); - return; + bad_request(response, request, e.what()); } } @@ -857,6 +963,8 @@ namespace confighttp { * @brief Unpair all clients. * @param response The HTTP response object. * @param request The HTTP request object. + * + * @api_examples{/api/clients/unpair-all| POST| null} */ void unpairAll(resp_https_t response, req_https_t request) { @@ -864,16 +972,12 @@ namespace confighttp { print_req(request); - pt::ptree outputTree; - - auto g = util::fail_guard([&]() { - std::ostringstream data; - pt::write_json(data, outputTree); - response->write(data.str()); - }); nvhttp::erase_all_clients(); proc::proc.terminate(); + + pt::ptree outputTree; outputTree.put("status", true); + send_response(response, outputTree); } /** @@ -886,6 +990,8 @@ namespace confighttp { * "uuid": "" * } * @endcode + * + * @api_examples{/api/unpair| POST| {"uuid":"1234"}} */ void unpair(resp_https_t response, req_https_t request) { @@ -896,25 +1002,18 @@ namespace confighttp { std::stringstream ss; ss << request->content.rdbuf(); - pt::ptree inputTree, outputTree; - - auto g = util::fail_guard([&]() { - std::ostringstream data; - pt::write_json(data, outputTree); - response->write(data.str()); - }); - try { // TODO: Input Validation + pt::ptree inputTree; + pt::ptree outputTree; pt::read_json(ss, inputTree); std::string uuid = inputTree.get("uuid"); outputTree.put("status", nvhttp::unpair_client(uuid)); + send_response(response, outputTree); } catch (std::exception &e) { BOOST_LOG(warning) << "Unpair: "sv << e.what(); - outputTree.put("status", false); - outputTree.put("error", e.what()); - return; + bad_request(response, request, e.what()); } } @@ -922,6 +1021,8 @@ namespace confighttp { * @brief Get the list of paired clients. * @param response The HTTP response object. * @param request The HTTP request object. + * + * @api_examples{/api/clients/list| GET| null} */ void listClients(resp_https_t response, req_https_t request) { @@ -929,26 +1030,21 @@ namespace confighttp { print_req(request); - pt::ptree named_certs = nvhttp::get_all_clients(); + const pt::ptree named_certs = nvhttp::get_all_clients(); pt::ptree outputTree; - outputTree.put("status", false); - - auto g = util::fail_guard([&]() { - std::ostringstream data; - pt::write_json(data, outputTree); - response->write(data.str()); - }); - outputTree.add_child("named_certs", named_certs); outputTree.put("status", true); + send_response(response, outputTree); } /** * @brief Close the currently running application. * @param response The HTTP response object. * @param request The HTTP request object. + * + * @api_examples{/api/apps/close| POST| null} */ void closeApp(resp_https_t response, req_https_t request) { @@ -956,16 +1052,11 @@ namespace confighttp { print_req(request); - pt::ptree outputTree; - - auto g = util::fail_guard([&]() { - std::ostringstream data; - pt::write_json(data, outputTree); - response->write(data.str()); - }); - proc::proc.terminate(); + + pt::ptree outputTree; outputTree.put("status", true); + send_response(response, outputTree); } void @@ -976,6 +1067,18 @@ namespace confighttp { auto address_family = net::af_from_enum_string(config::sunshine.address_family); https_server_t server { config::nvhttp.cert, config::nvhttp.pkey }; + server.default_resource["DELETE"] = [](resp_https_t response, req_https_t request) { + bad_request(response, request); + }; + server.default_resource["PATCH"] = [](resp_https_t response, req_https_t request) { + bad_request(response, request); + }; + server.default_resource["POST"] = [](resp_https_t response, req_https_t request) { + bad_request(response, request); + }; + server.default_resource["PUT"] = [](resp_https_t response, req_https_t request) { + bad_request(response, request); + }; server.default_resource["GET"] = not_found; server.resource["^/$"]["GET"] = getIndexPage; server.resource["^/pin/?$"]["GET"] = getPinPage; From 012a99c26da6d0eadb704f7c2e3a2fb5d42156bc Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 12 Jan 2025 08:44:54 -0500 Subject: [PATCH 14/20] chore: random cleanup (#3526) * chore: fix doxygen command typo * docs(app_examples): add note about built in resolution changing on Windows * docs(app_examples): update qres example * docs(readme): improve heading appearance * docs(readme): update minimum Fedora version * docs(app_examples): fix qres ref * docs(app_examples): use env variables for displayplacer * docs(app_examples): update nvidia-settings examples * Update dev.lizardbyte.app.Sunshine.metainfo.xml * docs(guides): redirect community guides to blog * docs(website): increase avatar padding --- README.md | 72 +- docs/Doxyfile | 2 +- docs/app_examples.md | 42 +- docs/getting_started.md | 2 +- docs/guides.md | 1098 +---------------- docs/images/discord_calls_01.png | Bin 28055 -> 0 bytes docs/images/discord_calls_02.png | Bin 39673 -> 0 bytes docs/images/discord_calls_03.png | Bin 28099 -> 0 bytes docs/images/discord_calls_04.png | Bin 96727 -> 0 bytes docs/images/discord_calls_05.png | Bin 97212 -> 0 bytes .../assets/img/navbar-avatar.png | Bin 20640 -> 17465 bytes .../dev.lizardbyte.app.Sunshine.metainfo.xml | 2 +- src/display_device.cpp | 2 +- 13 files changed, 76 insertions(+), 1144 deletions(-) delete mode 100644 docs/images/discord_calls_01.png delete mode 100644 docs/images/discord_calls_02.png delete mode 100644 docs/images/discord_calls_03.png delete mode 100644 docs/images/discord_calls_04.png delete mode 100644 docs/images/discord_calls_05.png diff --git a/README.md b/README.md index d225543a..57a5d9aa 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,49 @@ -# Overview +
+ +

Sunshine

+

Self-hosted game stream host for Moonlight.

+
-[![GitHub stars](https://img.shields.io/github/stars/lizardbyte/sunshine.svg?logo=github&style=for-the-badge)](https://github.com/LizardByte/Sunshine) -[![GitHub Releases](https://img.shields.io/github/downloads/lizardbyte/sunshine/total.svg?style=for-the-badge&logo=github)](https://github.com/LizardByte/Sunshine/releases/latest) -[![Docker](https://img.shields.io/docker/pulls/lizardbyte/sunshine.svg?style=for-the-badge&logo=docker)](https://hub.docker.com/r/lizardbyte/sunshine) -[![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) -[![GHCR](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)](https://github.com/LizardByte/Sunshine/pkgs/container/sunshine) -[![Winget Version](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==)](https://github.com/microsoft/winget-pkgs/tree/master/manifests/l/LizardByte/Sunshine) -[![Gurubase](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)](https://gurubase.io/g/sunshine) + -[![GitHub Workflow Status (CI)](https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/CI.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/Sunshine/actions/workflows/CI.yml?query=branch%3Amaster) -[![GitHub Workflow Status (localize)](https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/localize.yml.svg?branch=master&label=localize%20build&logo=github&style=for-the-badge)](https://github.com/LizardByte/Sunshine/actions/workflows/localize.yml?query=branch%3Amaster) -[![Read the Docs](https://img.shields.io/readthedocs/sunshinestream.svg?label=Docs&style=for-the-badge&logo=readthedocs)](http://sunshinestream.readthedocs.io) -[![Codecov](https://img.shields.io/codecov/c/gh/LizardByte/Sunshine?token=SMGXQ5NVMJ&style=for-the-badge&logo=codecov&label=codecov)](https://codecov.io/gh/LizardByte/Sunshine) - -LizardByte has the full documentation hosted on [Read the Docs](https://sunshinestream.readthedocs.io). - -## About +## ℹ️ About Sunshine is a self-hosted game stream host for Moonlight. Offering low latency, cloud gaming server capabilities with support for AMD, Intel, and Nvidia GPUs for hardware @@ -24,7 +51,12 @@ encoding. Software encoding is also available. You can connect to Sunshine from devices. A web UI is provided to allow configuration, and client pairing, from your favorite web browser. Pair from the local server or any mobile device. -## System Requirements +LizardByte has the full documentation hosted on [Read the Docs](https://app.readthedocs.org) + +* [Stable](https://sunshinestream.readthedocs.io/en/latest/) +* [Beta](https://sunshinestream.readthedocs.io/en/master/) + +## 🖥️ System Requirements @warning{These tables are a work in progress. Do not purchase hardware based on this information.} @@ -70,7 +102,7 @@ the local server or any mobile device.
- + @@ -152,7 +184,7 @@ the local server or any mobile device.
Linux/Debian: 12+ (bookworm)
Linux/Fedora: 39+Linux/Fedora: 40+
Linux/Ubuntu: 22.04+ (jammy)
-## Support +## ❓ Support Our support methods are listed in our [LizardByte Docs](https://lizardbyte.readthedocs.io/en/latest/about/support.html). diff --git a/docs/Doxyfile b/docs/Doxyfile index 6e95fd0f..5fa4ee6c 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -31,7 +31,7 @@ PROJECT_NAME = Sunshine # project specific settings DOT_GRAPH_MAX_NODES = 60 -IMAGE_PATH = ../docs/images +# IMAGE_PATH = ../docs/images PREDEFINED += SUNSHINE_BUILD_WAYLAND PREDEFINED += SUNSHINE_TRAY=1 diff --git a/docs/app_examples.md b/docs/app_examples.md index 015fb791..0db2ad93 100644 --- a/docs/app_examples.md +++ b/docs/app_examples.md @@ -167,7 +167,7 @@ process is killed.} | Undo | @code{}xrandr --output HDMI-1 --mode 3840x2160 --rate 120@endcode | @hint{The above only works if the xrandr mode already exists. You will need to create new modes to stream to macOS -and iOS devices, since they use non standard resolutions. +and iOS devices, since they use non-standard resolutions. You can update the ``Do`` command to this: ```bash @@ -257,22 +257,10 @@ hard-coding their corresponding number (e.g. ``kscreen-doctor output.HDMI-A1.mod ###### NVIDIA -| Prep Step | Command | -|-----------|-------------------------------------------------------------------------------------------------------------| -| Do | @code{}sh -c "${HOME}/scripts/set-custom-res.sh ${SUNSHINE_CLIENT_WIDTH} ${SUNSHINE_CLIENT_HEIGHT}"@endcode | -| Undo | @code{}sh -c "${HOME}/scripts/set-custom-res.sh 3840 2160"@endcode | - -The ``set-custom-res.sh`` will have this content: -```bash -#!/bin/bash -set -e - -# Get params and set any defaults -width=${1:-1920} -height=${2:-1080} -output=${3:-HDMI-1} -nvidia-settings -a CurrentMetaMode="${output}: nvidia-auto-select { ViewPortIn=${width}x${height}, ViewPortOut=${width}x${height}+0+0 }" -``` +| Prep Step | Command | +|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Do | @code{}sh -c "nvidia-settings -a CurrentMetaMode=\"HDMI-1: nvidia-auto-select { ViewPortIn=${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}, ViewPortOut=${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}+0+0 }\""@endcode | +| Undo | @code{}nvidia-settings -a CurrentMetaMode=\"HDMI-1: nvidia-auto-select { ViewPortIn=3840x2160, ViewPortOut=3840x2160+0+0 }"@endcode | ##### macOS @@ -281,21 +269,23 @@ nvidia-settings -a CurrentMetaMode="${output}: nvidia-auto-select { ViewPortIn=$ This tool can be installed following instructions in their [GitHub repository](https://github.com/jakehilborn/displayplacer)}. -| Prep Step | Command | -|-----------|----------------------------------------------------------------------------------------------------| -| Do | @code{}displayplacer "id: res:1920x1080 hz:60 scaling:on origin:(0,0) degree:0"@endcode | -| Undo | @code{}displayplacer "id: res:3840x2160 hz:120 scaling:on origin:(0,0) degree:0"@endcode | +| Prep Step | Command | +|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Do | @code{}sh -c "displayplacer \"id: res:${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT} hz:${SUNSHINE_CLIENT_FPS} scaling:on origin:(0,0) degree:0\""@endcode | +| Undo | @code{}displayplacer "id: res:3840x2160 hz:120 scaling:on origin:(0,0) degree:0"@endcode | ##### Windows +Sunshine has built-in support for changing the resolution and refresh rate on Windows. If you prefer to use a +third-party tool, you can use *QRes* as an example. ###### QRes @note{This example uses the *QRes* tool to change the resolution and refresh rate. -This tool can be downloaded from their [SourceForge repository](https://sourceforge.net/projects/qres).}. +This tool can be downloaded from their [SourceForge repository](https://sourceforge.net/projects/qres).} -| Prep Step | Command | -|-----------|-------------------------------------------------------------------------------------------------------------------------| -| Do | @code{}cmd /C FullPath\qres.exe /x:%SUNSHINE_CLIENT_WIDTH% /y:%SUNSHINE_CLIENT_HEIGHT% /r:%SUNSHINE_CLIENT_FPS%@endcode | -| Undo | @code{}cmd /C FullPath\qres.exe /x:3840 /y:2160 /r:120@endcode | +| Prep Step | Command | +|-----------|---------------------------------------------------------------------------------------------------------------------------| +| Do | @code{}cmd /C "FullPath\qres.exe /x:%SUNSHINE_CLIENT_WIDTH% /y:%SUNSHINE_CLIENT_HEIGHT% /r:%SUNSHINE_CLIENT_FPS%"@endcode | +| Undo | @code{}FullPath\qres.exe /x:3840 /y:2160 /r:120@endcode | ### Additional Considerations diff --git a/docs/getting_started.md b/docs/getting_started.md index 9281ab6a..7d915761 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -420,7 +420,7 @@ ssh @ 'startx &; export DISPLAY=:0; sunshine' @tip{You could also utilize the `~/.bash_profile` or `~/.bashrc` files to set up the `DISPLAY` variable.} -@seealso{ See [Remote SSH Headless Setup](md_docs_2guides.html#remote-ssh-headless-setup) +@seealso{ See [Remote SSH Headless Setup](https://app.lizardbyte.dev/2023-09-14-remote-ssh-headless-sunshine-setup) on how to set up a headless streaming server without autologin and dummy plugs (X11 + NVidia GPUs)} ### Configuration diff --git a/docs/guides.md b/docs/guides.md index 182399e9..d7271c7e 100644 --- a/docs/guides.md +++ b/docs/guides.md @@ -1,1099 +1,9 @@ # Guides -@admonition{Community | This collection of guides is written by the community! -Feel free to contribute your own tips and trips by making a PR.} - - -## Linux - -### Discord call cancellation - -| Author | [RickAndTired](https://github.com/RickAndTired) | -|------------|-------------------------------------------------| -| Difficulty | Easy | - -* Set your normal *Sound Output* volume to 100% - - ![](images/discord_calls_01.png) - -* Start Sunshine - -* Set *Sound Output* to *sink-sunshine-stereo* (if it isn't automatic) - - ![](images/discord_calls_02.png) - -* In Discord, right click *Deafen* and select your normal *Output Device*. - This is also where you will need to adjust output volume for Discord calls - - ![](images/discord_calls_03.png) - -* Open *qpwgraph* - - ![](images/discord_calls_04.png) - -* Connect `sunshine [sunshine-record]` to your normal *Output Device* - * Drag `monitor_FL` to `playback_FL` - * Drag `monitor_FR` to `playback_FR` - - ![](images/discord_calls_05.png) - -### Remote SSH Headless Setup - -| Author | [Eric Dong](https://github.com/e-dong) | -|------------|----------------------------------------| -| Difficulty | Intermediate | - -This is a guide to setup remote SSH into host to startup X server and Sunshine without physical login and dummy plug. -The virtual display is accelerated by the NVidia GPU using the TwinView configuration. - -@attention{This guide is specific for Xorg and NVidia GPUs. I start the X server using the `startx` command. -I also only tested this on an Artix runit init system on LAN. -I didn't have to do anything special with pulseaudio (pipewire untested). - -Pipewire does not seem to work when Sunshine is started over an SSH session. -A workaround to this problem is to kill the Sunshine instance started via SSH, and start a new one -with the permissions of the desktop session. See [Autostart on boot without auto-login](#autostart-on-boot-without-auto-login) - -Keep your monitors plugged in until the [Checkpoint](#checkpoint) step.} - -@tip{Prior to editing any system configurations, you should make a copy of the original file. -This will allow you to use it for reference or revert your changes easily.} - -#### The Big Picture -Once you are done, you will need to perform these 3 steps: - -1. Turn on the host machine -2. Start Sunshine on remote host with a script that: - - * Edits permissions of `/dev/uinput` (added sudo config to execute script with no password prompt) - * Starts X server with `startx` on virtual display - * Starts Sunshine - -3. Startup Moonlight on the client of interest and connect to host - -@hint{As an alternative to SSH... - -**Step 2** can be replaced with autologin and starting Sunshine as a service or putting -`sunshine &` in your `.xinitrc` file if you start your X server with `startx`. -In this case, the workaround for `/dev/uinput` permissions is not needed because the udev rule would be triggered -for "physical" login. See [Linux Setup](md_docs_2getting__started.html#linux). I personally think autologin compromises -the security of the PC, so I went with the remote SSH route. I use the PC more than for gaming, so I don't need a -virtual display everytime I turn on the PC (E.g running updates, config changes, file/media server).} - -First we will [setup the host](#host-setup) and then the [SSH Client](#ssh-client-setup) -(Which may not be the same as the machine running the moonlight client). - -#### Host Setup -We will be setting up: - -1. [Static IP Setup](#static-ip-setup) -2. [SSH Server Setup](#ssh-server-setup) -3. [Virtual Display Setup](#virtual-display-setup) -4. [Uinput Permissions Workaround](#uinput-permissions-workaround) -5. [Stream Launcher Script](#stream-launcher-script) - -#### Static IP Setup -Setup static IP Address for host. For LAN connections you can use DHCP reservation within your assigned range. -e.g. 192.168.x.x. This will allow you to ssh to the host consistently, so the assigned IP address does -not change. It is preferred to set this through your router config. - -#### SSH Server Setup -@note{Most distros have OpenSSH already installed. If it is not present, install OpenSSH using your package manager.} - -@tabs{ - @tab{Debian based | ```bash - sudo apt update - sudo apt install openssh-server - ```} - @tab{Arch based | ```bash - sudo pacman -S openssh - # Install openssh- if you are not using SystemD - # e.g. sudo pacman -S openssh-runit - ```} - @tab{Alpine based | ```bash - sudo apk update - sudo apk add openssh - ```} - @tab{Fedora based (dnf) | ```bash - sudo dnf install openssh-server - ```} - @tab{Fedora based (yum) | ```bash - sudo yum install openssh-server - ```} -} - -Next make sure the OpenSSH daemon is enabled to run when the system starts. - -@tabs{ - @tab{SystemD | ```bash - sudo systemctl enable sshd.service - sudo systemctl start sshd.service # Starts the service now - sudo systemctl status sshd.service # See if the service is running - ```} - @tab{Runit | ```bash - sudo ln -s /etc/runit/sv/sshd /run/runit/service # Enables the OpenSSH daemon to run when system starts - sudo sv start sshd # Starts the service now - sudo sv status sshd # See if the service is running - ```} - @tab{OpenRC | ```bash - rc-update add sshd # Enables service - rc-status # List services to verify sshd is enabled - rc-service sshd start # Starts the service now - ```} -} - -**Disabling PAM in sshd** - -I noticed when the ssh session is disconnected for any reason, `pulseaudio` would disconnect. -This is due to PAM handling sessions. When running `dmesg`, I noticed `elogind` would say removed user session. -In this [Gentoo Forums post](https://forums.gentoo.org/viewtopic-t-1090186-start-0.html), -someone had a similar issue. Starting the X server in the background and exiting out of the console would cause your -session to be removed. - -@caution{According to this [article](https://devicetests.com/ssh-usepam-security-session-status) -disabling PAM increases security, but reduces certain functionality in terms of session handling. -*Do so at your own risk!*} - -Edit the ``sshd_config`` file with the following to disable PAM. - -```txt -usePAM no -``` - -After making changes to the `sshd_config`, restart the sshd service for changes to take effect. - -@tip{Run the command to check the ssh configuration prior to restarting the sshd service. -```bash -sudo sshd -t -f /etc/ssh/sshd_config -``` - -An incorrect configuration will prevent the sshd service from starting, which might mean -losing SSH access to the server.} - -@tabs{ - @tab{SystemD | ```bash - sudo systemctl restart sshd.service - ```} - @tab{Runit | ```bash - sudo sv restart sshd - ```} - @tab{OpenRC | ```bash - sudo rc-service sshd restart - ```} -} - -#### Virtual Display Setup -As an alternative to a dummy dongle, you can use this config to create a virtual display. - -@important{This is only available for NVidia GPUs using Xorg.} - -@hint{Use ``xrandr`` to see name of your active display output. Usually it starts with ``DP`` or ``HDMI``. For me, it is ``DP-0``. -Put this name for the ``ConnectedMonitor`` option under the ``Device`` section. - -```bash -xrandr | grep " connected" | awk '{ print $1 }' -``` -} - -```xorg -Section "ServerLayout" - Identifier "TwinLayout" - Screen 0 "metaScreen" 0 0 -EndSection - -Section "Monitor" - Identifier "Monitor0" - Option "Enable" "true" -EndSection - -Section "Device" - Identifier "Card0" - Driver "nvidia" - VendorName "NVIDIA Corporation" - Option "MetaModes" "1920x1080" - Option "ConnectedMonitor" "DP-0" - Option "ModeValidation" "NoDFPNativeResolutionCheck,NoVirtualSizeCheck,NoMaxPClkCheck,NoHorizSyncCheck,NoVertRefreshCheck,NoWidthAlignmentCheck" -EndSection - -Section "Screen" - Identifier "metaScreen" - Device "Card0" - Monitor "Monitor0" - DefaultDepth 24 - Option "TwinView" "True" - SubSection "Display" - Modes "1920x1080" - EndSubSection -EndSection -``` - -@note{The `ConnectedMonitor` tricks the GPU into thinking a monitor is connected, -even if there is none actually connected! This allows a virtual display to be created that is accelerated with -your GPU! The `ModeValidation` option disables valid resolution checks, so you can choose any -resolution on the host! - -**References** - -* [issue comment on virtual-display-linux](https://github.com/dianariyanto/virtual-display-linux/issues/9#issuecomment-786389065) -* [Nvidia Documentation on Configuring TwinView](https://download.nvidia.com/XFree86/Linux-x86/270.29/README/configtwinview.html) -* [Arch Wiki Nvidia#TwinView](https://wiki.archlinux.org/title/NVIDIA#TwinView) -* [Unix Stack Exchange - How to add virtual display monitor with Nvidia proprietary driver](https://unix.stackexchange.com/questions/559918/how-to-add-virtual-monitor-with-nvidia-proprietary-driver) -} - -#### Uinput Permissions Workaround - -##### Steps -We can use `chown` to change the permissions from a script. Since this requires `sudo`, -we will need to update the sudo configuration to execute this without being prompted for a password. - -1. Create a `sunshine-setup.sh` script to update permissions on `/dev/uinput`. Since we aren't logged into the host, - the udev rule doesn't apply. -2. Update user sudo configuration `/etc/sudoers.d/` to allow the `sunshine-setup.sh` - script to be executed with `sudo`. - -@note{After I setup the :ref:`udev rule ` to get access to `/dev/uinput`, I noticed when I sshed -into the host without physical login, the ACL permissions on `/dev/uinput` were not changed. So I asked -[reddit](https://www.reddit.com/r/linux_gaming/comments/14htuzv/does_sshing_into_host_trigger_udev_rule_on_the). -I discovered that SSH sessions are not the same as a physical login. -I suppose it's not possible for SSH to trigger a udev rule or create a physical login session.} - -##### Setup Script -This script will take care of any preconditions prior to starting up Sunshine. - -Run the following to create a script named something like `sunshine-setup.sh`: - -```bash -echo "chown $(id -un):$(id -gn) /dev/uinput" > sunshine-setup.sh && \ - chmod +x sunshine-setup.sh -``` - -(**Optional**) To Ensure ethernet is being used for streaming, you can block Wi-Fi with `rfkill`. - -Run this command to append the rfkill block command to the script: - -```bash -echo "rfkill block $(rfkill list | grep "Wireless LAN" \ - | sed 's/^\([[:digit:]]\).*/\1/')" >> sunshine-setup.sh -``` - -##### Sudo Configuration -We will manually change the permissions of `/dev/uinput` using `chown`. -You need to use `sudo` to make this change, so add/update the entry in `/etc/sudoers.d/${USER}`. - -@danger{Do so at your own risk! It is more secure to give sudo and no password prompt to a single script, -than a generic executable like chown.} - -@warning{Be very careful of messing this config up. If you make a typo, *YOU LOSE THE ABILITY TO USE SUDO*. -Fortunately, your system is not borked, you will need to login as root to fix the config. -You may want to setup a backup user / SSH into the host as root to fix the config if this happens. -Otherwise, you will need to plug your machine back into a monitor and login as root to fix this. -To enable root login over SSH edit your SSHD config, and add `PermitRootLogin yes`, and restart the SSH server.} - -1. First make a backup of your `/etc/sudoers.d/${USER}` file. - - ```bash - sudo cp /etc/sudoers.d/${USER} /etc/sudoers.d/${USER}.backup - ``` - -2. `cd` to the parent dir of the `sunshine-setup.sh` script and take note of the full filepath. -3. Execute the following to edit your sudoer config file. - -@danger{NEVER modify a file in ``sudoers.d`` directly. Always use the ``visudo`` command. This command checks your changes -before saving the file, and if the resulting changes would break sudo on your system, it will prompt you to fix -them. Modifying the file with nano or vim directly does not give you this sanity check and introduces the -possibility of losing sudo access to your machine. Tread carefully, and make a backup.} - -```bash -sudo visudo /etc/sudoers.d/${USER} -``` - -Copy the below configuration into the text editor. Change `${USER}` wherever it occurs to your username -(e.g. if your username is `sunshineisaawesome` you should change `${USER}` to `sunshineisawesome`) -or modify the path if you placed `sunshine-setup.sh` in a different area. - -``` -${USER} ALL=(ALL:ALL) ALL, NOPASSWD: /home/${USER}/scripts/sunshine-setup.sh -``` - -These changes allow the script to use sudo without being prompted with a password. - -e.g. `sudo $(pwd)/sunshine-setup.sh` - -#### Stream Launcher Script -This is the main entrypoint script that will run the `sunshine-setup.sh` script, start up X server, and Sunshine. -The client will call this script that runs on the host via ssh. - - -##### Sunshine Startup Script -This guide will refer to this script as `~/scripts/sunshine.sh`. -The setup script will be referred as `~/scripts/sunshine-setup.sh`. - -```bash -#!/bin/bash -set -e - -export DISPLAY=:0 - -# Check existing X server -ps -e | grep X >/dev/null -[[ ${?} -ne 0 ]] && { - echo "Starting X server" - startx &>/dev/null & - [[ ${?} -eq 0 ]] && { - echo "X server started successfully" - } || echo "X server failed to start" -} || echo "X server already running" - -# Check if sunshine is already running -ps -e | grep -e .*sunshine$ >/dev/null -[[ ${?} -ne 0 ]] && { - sudo ~/scripts/sunshine-setup.sh - echo "Starting Sunshine!" - sunshine > /dev/null & - [[ ${?} -eq 0 ]] && { - echo "Sunshine started successfully" - } || echo "Sunshine failed to start" -} || echo "Sunshine is already running" - -# Add any other Programs that you want to startup automatically -# e.g. -# steam &> /dev/null & -# firefox &> /dev/null & -# kdeconnect-app &> /dev/null & -``` - -#### SSH Client Setup -We will be setting up: - -1. [SSH Key Authentication Setup](#ssh-key-authentication-setup) -2. [SSH Client Script (Optional)](#ssh-client-script-optional) - -##### SSH Key Authentication Setup -1. Setup your SSH keys with `ssh-keygen` and use `ssh-copy-id` to authorize remote login to your host. - Run `ssh @` to login to your host. - SSH keys automate login so you don't need to input your password! -2. Optionally setup a `~/.ssh/config` file to simplify the `ssh` command - - ```txt - Host - Hostname - User - IdentityFile ~/.ssh/ - ``` - - Now you can use `ssh `. - `ssh ` will execute the command or script on the remote host. - -##### Checkpoint -As a sanity check, let's make sure your setup is working so far! - -###### Test Steps -With your monitor still plugged into your Sunshine host PC: - -1. `ssh ` -2. `~/scripts/sunshine.sh` -3. `nvidia-smi` - - You should see the Sunshine and Xorg processing running: - - ```bash - nvidia-smi - ``` - - *Output:* - ```txt - +---------------------------------------------------------------------------------------+ - | NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 | - |-----------------------------------------+----------------------+----------------------+ - | GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | - | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | - | | | MIG M. | - |=========================================+======================+======================| - | 0 NVIDIA GeForce RTX 3070 Off | 00000000:01:00.0 On | N/A | - | 30% 46C P2 45W / 220W | 549MiB / 8192MiB | 2% Default | - | | | N/A | - +-----------------------------------------+----------------------+----------------------+ - - +---------------------------------------------------------------------------------------+ - | Processes: | - | GPU GI CI PID Type Process name GPU Memory | - | ID ID Usage | - |=======================================================================================| - | 0 N/A N/A 1393 G /usr/lib/Xorg 86MiB | - | 0 N/A N/A 1440 C+G sunshine 293MiB | - +---------------------------------------------------------------------------------------+ - ``` - -4. Check `/dev/uinput` permissions - - ```bash - ls -l /dev/uinput - ``` - - *Output:* - - ```console - crw------- 1 10, 223 Aug 29 17:31 /dev/uinput - ``` - -5. Connect to Sunshine host from a moonlight client - -Now kill X and Sunshine by running `pkill X` on the host, unplug your monitors from your GPU, and repeat steps 1 - 5. -You should get the same result. -With this setup you don't need to modify the Xorg config regardless if monitors are plugged in or not. - -```bash -pkill X -``` - -##### SSH Client Script (Optional) -At this point you have a working setup! For convenience, I created this bash script to automate the -startup of the X server and Sunshine on the host. -This can be run on Unix systems, or on Windows using the `git-bash` or any bash shell. - -For Android/iOS you can install Linux emulators, e.g. `Userland` for Android and `ISH` for iOS. -The neat part is that you can execute one script to launch Sunshine from your phone or tablet! - -```bash -#!/bin/bash -set -e - -ssh_args="@192.168.X.X" # Or use alias set in ~/.ssh/config - -check_ssh(){ - result=1 - # Note this checks infinitely, you could update this to have a max # of retries - while [[ $result -ne 0 ]] - do - echo "checking host..." - ssh $ssh_args "exit 0" 2>/dev/null - result=$? - [[ $result -ne 0 ]] && { - echo "Failed to ssh to $ssh_args, with exit code $result" - } - sleep 3 - done - echo "Host is ready for streaming!" -} - -start_stream(){ - echo "Starting sunshine server on host..." - echo "Start moonlight on your client of choice" - # -f runs ssh in the background - ssh -f $ssh_args "~/scripts/sunshine.sh &" -} - -check_ssh -start_stream -exit_code=${?} - -sleep 3 -exit ${exit_code} -``` - -#### Next Steps -Congratulations, you can now stream your desktop headless! When trying this the first time, -keep your monitors close by incase something isn't working right. - -@seealso{Now that you have a virtual display, you may want to automate changing the resolution -and refresh rate prior to connecting to an app. See -[Changing Resolution and Refresh Rate](md_docs_2app__examples#changing-resolution-and-refresh-rate) -for more information.} - -### Autostart on boot without auto-login - -| Author | [MidwesternRodent](https://github.com/midwesternrodent) | -| ---------- | ------------------------------------------------------- | -| Difficulty | Intermediate | - -After following this guide you will be able to: -1. Turn on the Sunshine host via Moonlight's Wake on LAN (WoL) feature. -2. Have Sunshine initialize to the login screen ready for you to enter your credentials. -3. Login to your desktop session remotely, and have your pipewire audio and Sunshine tray icon work appropriately. - -#### Specifications -This guide was created with the following software on the host: -1. OpenSSH server and client (both on the host machine) -2. Sunshine v2024.1003.1754422 -3. Debian 12 w/ KDE Plasma, SDDM, Wayland (also tested through xorg), and pipewire for audio. - -The host hardware that was used in developing this guide: -1. AMD 7900XTX -2. AMD Ryzen 7 7800X3D -3. 128GB DDR5 RAM -4. 4 displays in total. 2 1080p displays, 1 3440x1440 display, and 1 4k Roku TV which is used as the always-on display -for streaming. (could be subbed with a dummy plug). - -If you have used this guide on any alternative hardware or software (including non-debian based distros) -please, feel free to modify this guide and keep it growing! - -#### Caveats -1. When you login the machine will close your connection and you will have to reconnect. This is necessary due to an -issue similar to why the [Uinput Permissions Workaround](#uinput-permissions-workaround) is needed since SSH -connections are not treated the same as graphical logins. This causes weirdness like sound not working through -pipewire, and the tray icon for Sunshine not appearing. To get around this, we need to close the SSH initiated Sunshine -service, and start a new Sunshine service with the permissions of the graphical desktop. Unfortunately, this closes the -connection and forces you to reconnect through Moonlight. There is no way around this to the best of my knowledge -without enabling auto-login. -3. This guide does not cover using virtual displays. If you are using Nvidia graphics, -see [Remote SSH Headless Setup](#remote-ssh-headless-setup). If you are using AMD hardware, let me know -if you find something or feel free to add it to this guide. -4. I haven't (yet) found a way to disable sleep on the login screen, so if you wait too long after starting your PC, -the display may go to sleep and Moonlight will have trouble connecting. Shutting down and using WoL works great -though. - -@attention{This is definitely safer than enabling auto-login directly, especially for a dual-use PC that is not only -streamed via Moonlight, but is also used as a standard desktop. *However* I do not know the implications of having an -always running SSH client to the localhost on the same machine. It may be possible for someone with significant -knowledge and physical access to the machine to compromise your user account due to this always-running SSH session. -However, that's better than just having the desktop always available, or opening up SSH even just your LAN since this -guide specifically disables non-localhost connections, so I believe this is safer to use than auto-login for general -users. As always, your [threat model](https://en.wikipedia.org/wiki/Threat_model) may vary.} - -#### Prerequisites -In [Remote SSH Headless Setup](#remote-ssh-headless-setup) complete the following sections. - -1. [Static IP Setup](#static-ip-setup) -2. [SSH Server Setup](#ssh-server-setup) -3. [Virtual Display Setup](#virtual-display-setup) -4. [Uinput Permissions Workaround](#uinput-permissions-workaround) -5. [Stream Launcher Script](#stream-launcher-script) - -@note{On a default Debian 12 install using KDE Plasma, you are using the Simple Desktop Display Manager (SDDM). -Even if you are logging in to a Wayland session, SDDM by default starts with an xorg session, so this script -does not need to be modified if you primarily use a Wayland session (the default) when you login.} - -#### Instructions - -##### Enable Wake on LAN - -Wake on LAN (WoL) will allow you to send a magic packet to turn your PC on remotely. This is handled automatically by -Moonlight's "send wake on lan" option in the app but you do need to enable it on your host machine first. The -[instructions on the debian.org](https://wiki.debian.org/WakeOnLan#Enabling_WOL) site are a little hard to parse, so -I've simplified them below. - -@note{This may not work on all deb based distributions. If you know of a better way for POP OS, Ubuntu, or another -debian based distro please feel free to edit the guide yourself, or let me know.} - -First, find the name of your ethernet interface. - -```bash -ip link show -``` - -When I run this command, these are the results I receive -``` -1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 -   link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 -2: enp117s0: mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 -   link/ether 9c:6b:00:59:33:c1 brd ff:ff:ff:ff:ff:ff -``` - -We can ignore the loopback interface, and I can see my ethernet interface is called `enp117s0`. You might see -wireless interfaces here as well but they can also be ignored. - -@note{If your PC is only connected via Wi-Fi, it is still technically possible to get this working, but it is outside -the scope of this guide and requires more networking knowledge and a Wi-Fi chip that supports WoL. If this is your -first foray into linux, I'd recommend just getting a cable.} - -Now I can install ethtool and modify my interface to allow Wake on LAN. For your use, replace `enp117s0` with whatever -the name of your ethernet interface is from the command `ip link show` - -```bash -sudo apt update -sudo apt install ethtool -sudo ethtool -s enp117s0 wol g -``` - -##### SSH Client Setup -To start, we need to install an SSH client (which is different from the *server* in [Remote SSH Headless Setup](#remote-ssh-headless-setup)) -on our machine if this not already done. Open a terminal and enter the following commands. - -```bash -sudo apt update -sudo apt install openssh-client -``` - -Next we need to generate the keys we will use to connect to our SSH session. This is as simple as running the -following in a terminal: - -```bash -ssh-keygen -``` - -and simply pressing enter through the default options. This will place a file called `id_rsa` and `id_rsa.pub` -in the hidden folder `~/.ssh/`. This is the default key used when this user initiates an SSH session. - -Next, we'll copy that public key to the `~/.ssh/authorized_users` file. These are the public keys -allowed to access this machine over SSH, and will allow us to establish an SSH connection with this user -to the SSH server running on the localhost. - -```bash -cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys -``` - -@tip{If you also want any other machines (e.g. a laptop or Steam Deck) to connect to your machine remotely over ssh, -be sure to generate a pubkey on that machine and append it to the authorized_keys file like we did above.} - -###### SSH Server Modifications - -We'll want to make a few modification to the SSH server on the Sunshine host, both for convenience and security. - -Modify `/etc/ssh/sshd_config` with the following changes: - -@tabs{ - @tab{nano | ```bash - sudo nano /etc/ssh/sshd_config - ```} - @tab{vim | ```bash - sudo vi /etc/ssh/sshd_config - ```} -} - -Find the line with `PasswordAuthentication` and make sure it is set to `no` (removed the `#` if present. -Then find the line `PubkeyAuthentication` and make sure it is set to `yes` and remove the `#` from the beginning -if present. When you're done you should have these two lines in your config somewhere. - -``` -PubkeyAuthentication yes -PasswordAuthentication no -``` - -@tip{Using publickey encryption for SSH connections significantly increases your protection against brute force -attacks, and protects you against a rogue machine pretending to be your SSH server and stealing your password.} - -The next step is optional, but if you do not plan on connecting to your computer remotely via ssh and only have -installed SSH for the purposes of using Sunshine, it's a good idea to disable listening for remote SSH connections. -Do this by changing the following lines near the top of your ``sshd_config``: - -``` -#ListenAddress 0.0.0.0 -#ListenAddress :: -``` - -To the following: - -``` -ListenAddress 127.0.0.1 -ListenAddress ::1 -``` - -This will only allow SSH connections coming from your computer itself. - -@tip{on some distributions, the maintainers have added some files in ``/etc/ssh/sshd_config.d/`` which are pulled into -your ``sshd_config``. These modifications can conflict with what we've just done. If you have any files in -``/etc/ssh/sshd_config.d/``, make sure they do not include any of the changes we've just made or you will experience -problems. If they do, you can comment out those lines by placing a ``#`` at their beginning, or delete the files safely -if you don't plan to use SSH for anything other than Sunshine.} - -###### Quick Test and Accept Host Authenticity. - -Next, let's reboot the machine and try to connect! Accept any warnings about the unidentified host at this time, -you'll never see those appear again unless something changes with your setup. - -```bash -ssh $(whoami)@localhost -``` - -You should see a new login prompt for the machine you're already on, and when you type `exit` you should just see - -```bash -logout -Connection to localhost closed. -``` - -##### Run sunshine-setup on boot over SSH - -Thanks to [this comment from Gavin Haynes on Unix Stack exchange](https://unix.stackexchange.com/questions/669389/how-do-i-get-an-ssh-command-to-run-on-boot/669476#669476), -we can establish an SSH connection on boot to run the sunshine-setup script via a systemd service. - -###### Disable default Sunshine services - -These service files are sometimes overwritten when updating Sunshine with the .deb. -So we'll be making new ones and disabling the included service files for our purposes. - -``` -sudo sytstemctl disable sunshine -systemctl --user disable sunshine -``` - -@note{In order to disable the user service, you must be logged in to the graphical desktop environment and run the -command from a GUI terminal. You'll also likely need to approve a polkit request (a graphical popup that asks -for your password). Trying to disable the user service without being signed in to the graphical environment is a -recipe for pain, and is why ``sudo`` is not invoked on the second line in the command above.} - -###### Create the autossh-sunshine-start script - -@tabs{ - @tab{nano | ```bash - sudo nano /usr/local/bin/autossh-sunshine-start - ```} - @tab{vim | ```bash - sudo vi /usr/local/bin/autossh-sunshine-start - ```} -} - -Copy the below script to that location and replace `{USERNAME}` wherever it occurs with the username you created -the SSH public key for in the previous section. - -```bash -#!/bin/bash -ssh -i /home/{USERNAME}/.ssh/id_rsa {USERNAME}@localhost -"/home/{USERNAME}/scripts/sunshine.sh" -``` - -@attention{This script uses the location of the script in [Stream Launcher Script](#stream-launcher-script). -Please complete that section before continuing.} - -Once you've created the script, be sure to make it executable by running: - -```bash -sudo chmod +x /usr/local/bin/autossh-sunshine-start -``` - -###### Create the autossh systemd service file - -@tabs{ - @tab{nano | ```bash - sudo nano /etc/systemd/system/autossh-sunshine.service - ```} - @tab{vim | ```bash - sudo vi /etc/systemd/system/autossh-sunshine.service - ```} -} - -Copy and paste the below systemd file and save it to the location in the commands above. - -``` -[Unit] -Description=Start sunshine over an localhost SSH connection on boot -Requires=sshd.service -After=sshd.service - -[Service] -ExecStartPre=/bin/sleep 5 -ExecStart=/usr/local/bin/autossh-sunshine-start -Restart=on-failure -RestartSec=5s - -[Install] -WantedBy=multi-user.target -``` - -Make it executable, and enable the service when you're done. - -```bash -sudo chmod +x /etc/systemd/system/autossh-sunshine.service -sudo systemctl start autossh-sunshine -sudo systemctl enable autossh-sunshine -``` - -This point is a good time for a sanity check, so restart your PC and try to sign in to your desktop via Moonlight. -You should be able to access the login screen, enter your credentials, and control the user session. At this point -you'll notice the reason for the next section as your audio will be non-functional and you won't see any tray icon -for Sunshine. If you don't care about audio (and maybe a couple other bugs you might encounter from time to time due -to the permissions difference between an SSH session and the desktop session), you can consider yourself finished at -this point! - -@note{You might also notice some issues if you have multiple monitors setup (including the dummy plug), like the mouse -cursor not being on the right screen for you to login. We will address this in the last step of this guide. It requires -messing with some configs for SDDM.} - -##### Getting the audio working - -To get the audio (and tray icon, etc...) working we will create a systemd user service, that will start on a graphical -login, kill the autossh-sunshine system service, and start Sunshine just like the standard Sunshine service. -This service will also need to call the autossh-sunshine system service before it is stopped as the user service will -be killed when we log out of the graphical session, so we want to make sure we restart that SSH service so we don't -lose the ability to log back in if we need to. - -@tabs{ - @tab{nano | ```bash - sudo nano /usr/lib/systemd/user/sunshine-after-login.service - ```} - @tab{vim | ```bash - sudo vi /usr/lib/systemd/user/sunshine-after-login.service - ```} -} - -Once again, copy the below service file into your text editor at the location above. - -``` -[Unit] -Description=Start Sunshine with the permissions of the graphical desktop session -StartLimitIntervalSec=500 -StartLimitBurst=5 - -[Service] -# Avoid starting Sunshine before the desktop is fully initialized. -ExecStartPre=/usr/bin/pkill sunshine -ExecStartPre=/bin/sleep 5 -ExecStart=/usr/bin/sunshine -ExecStopPost=/usr/bin/systemctl start autossh-sunshine - -Restart=on-failure -RestartSec=5s - -[Install] -WantedBy=xdg-desktop-autostart.target -``` - -Make it executable, and enable it. - -```bash -sudo chmod +x /usr/lib/systemd/user/sunshine-after-login.service -systemctl --user enable sunshine-after-login -``` - -###### Polkit Rules for Sunshine User Service - -Since this is being run with the permissions of the graphical session, we need to make a polkit modification to allow -it to call the system service autossh-sunshine when this user service is stopped, without prompting us for a password. - -@tabs{ - @tab{nano | ```bash - sudo nano /etc/polkit-1/rules.d/sunshine.rules - ```} - @tab{vim | ```bash - sudo vi /etc/polkit-1/rules.d/sunshine.rules - ```} -} - -Once again, copy the below to the .rules file in your text editor. - -```js -polkit.addRule(function(action, subject) { -   if (action.id == "org.freedesktop.systemd1.manage-units" && -       action.lookup("unit") == "autossh-sunshine.service") -   { -       return polkit.Result.YES; -   } -}) -``` - -###### Modifications to sudoers.d files - -Lastly, we need to make a few modifications to the sudoers file for our users. Replace {USERNAME} below with your -username. You will be prompted to select either vi or nano for your editor if you've not used this command before, -choose whichever you prefer. - -``` -sudo visudo /etc/sudoers.d/{USERNAME} -``` - -@danger{NEVER modify a file in ``sudoers.d`` directly. Always use the ``visudo`` command. This command checks your changes -before saving the file, and if the resulting changes would break sudo on your system, it will prompt you to fix -them. Modifying the file with nano or vim directly does not give you this sanity check and introduces the -possibility of losing sudo access to your machine. Tread carefully, and make a backup.} - -As always, copy and paste the below into your user's `sudoers.d` configuration. Replace {USERNAME} with your username, -and {HOSTNAME} with the name of your computer. - -``` -{USERNAME} {HOSTNAME} = (root) NOPASSWD: /home/{USERNAME}/scripts/sunshine-setup.sh -{USERNAME} {HOSTNAME} = (root) NOPASSWD: /bin/sunshine -{USERNAME} {HOSTNAME} = (root) NOPASSWD: /usr/bin/systemctl start autossh-sunshine -{USERNAME} {HOSTNAME} = (root) NOPASSWD: /usr/bin/systemctl --user start sunshine-after-login -# The below is optional, but will allow us to send trigger a shutdown with a sunshine prep command, if desired. -{USERNAME} {HOSTNAME} = (root) NOPASSWD: /usr/sbin/shutdown -``` - -Once again, restart your computer and do a quick test. Make sure you can connect to the PC to login and enter your -credentials. You should be booted out of the system, and then can reconnect a few seconds later to the logged-in -desktop session. You should see a tray icon for Sunshine, and the sound should be working (or you may need to manually -select the sunshine-sink at least the first time). - -If you don't have multiple monitors, at this point you can consider yourself done! - -##### Configuring the login screen layout for multiple monitors - -This is not Sunshine specific, but is a frequent problem I had setting up Sunshine and thought it pertinent to add to -the guide. If you are using multiple monitors (even a single monitor with a dummy plug may have this problem) you -might notice the streamed login screen has one or more of the following problems: - -1. The text is way too small to see (caused by a too-high resolution) -2. The mouse cursor is off on some other screen (caused by not mirroring the displays) -3. There are multiple login screens overlapping each other (caused by differing resolutions, and trying to mirror -the display). - -###### Log in to an X11 Session - -This can be fixed, by modifying some scripts called by SDDM on boot. To start though, we need to make sure we're -logged into an x11 session, not Wayland or the terminal. As the Wayland session will give us incorrect information, -and the terminal will give us no information since no graphical environment exists. SDDM initially starts an x11 -session to display the login screen so we need to use xorg commands to change the display configuration. - -To do this, log out of your desktop session on the Sunshine host, and somewhere on the lower left of your screen -(depending on your SDDM theme) there will be some text that on Debian 12 KDE Plasma defaults to saying -`Session: Plasma (Wayland)`. Select this and choose `Plasma (X11)` from the drop down menu and sign in. - -###### Find your monitor identifiers. - -Open a terminal and run: - -```bash -xrandr | grep -w connected -``` - -This will require some more sleuthing on your part. Different PC hardware, and different monitors / connectors, -display the names differently. Some start at 0, some start 1. Some spell out "DisplayPort" some, say "DP". You will -need to modify all of the commands from here on out based on the output of the above command. I will use the output I -receive below as the example for the rest of this guide. - -```bash -DisplayPort-0 connected (normal left inverted right x axis y axis) -DisplayPort-1 connected (normal left inverted right x axis y axis) -DisplayPort-2 connected (normal left inverted right x axis y axis) -HDMI-A-0 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 800mm x 450mm -``` - -@note{If I instead run this command on Wayland, I get the following useless output. Hence the need to sign in to an -x11 session. - -```bash -XWAYLAND0 connected 2592x1458+6031+0 (normal left inverted right x axis y axis) 600mm x 340mm -XWAYLAND1 connected 2592x1458+0+0 (normal left inverted right x axis y axis) 480mm x 270mm -XWAYLAND2 connected primary 3440x1440+2592+0 (normal left inverted right x axis y axis) 800mm x 330mm -XWAYLAND3 connected 2592x1458+0+0 (normal left inverted right x axis y axis) 1440mm x 810mm - -``` -} - - -From this, you can see that my monitors are named the following under an x11 session. - -DisplayPort-0 -DisplayPort-1 -DisplayPort-2 -HDMI-A-0 - -@tip{If you have a label maker, now would be a good time to unplug some cables, determine where they are on your -system, and label the outputs on your graphics card to ease changing your setup in the future.} - -In my setup, after moving some inputs I changed my system so that these cables correspond to the below monitors - -| Display Name | Monitor | -| ------------- | --------------------------- | -| DisplayPort-0 | rightmost 1080p display | -| DisplayPort-1 | leftmost 1080p display | -| DisplayPort-2 | middle 3440x1440 display | -| HDMI-A-0 | 4k Roku TV (and dummy plug) | - -###### Modify the SDDM startup script - -For my purposes, I would prefer to have the Roku TV (which doubles as my always-on dummy plug) to always display a -1080p screen on login (this can be changed automatically after login). And I would like to retain the ability to use -my leftmost monitor to login to my physical desktop, but I'd like to disable my primary and rightmost displays. - -To do this, we need to modify the SDDM startup script to shut off DisplayPort-1 and DisplayPort-2, set HDMI-A-0 to -1080p and mirror it with DisplayPort-1. - -@tabs{ - @tab{nano | ```bash - sudo nano /usr/share/sddm/scripts/Xsetup - ```} - @tab{vim | ```bash - sudo vi /usr/share/sddm/scripts/Xsetup - ```} -} - -Which will open a script that looks like this. We will not be removing these lines. - -```bash -#!/bin/sh -# Xsetup - run as root before the login dialog appears - -if [ -e /sbin/prime-offload ]; then -   echo running NVIDIA Prime setup /sbin/prime-offload -   /sbin/prime-offload -fi -``` - -At the bottom of this Xsetup script though, we can add some xrandr commands - -To shut a display off, we can use: `xrandr --output {DISPLAYNAME} --off`. - -To set a display as the primary and accept -it's automatic (usually the maximum, but not always especially on TVs where the default refresh rate may be lower) -resolution and refresh rate we can use: `xrandr --output {DISPLAYNAME} --auto --primary`. - -To set a display to a specific resolution we can use: `xrandr --output {DISPLAYNAME} --mode {PIXELWIDTH}x{PIXELLENGTH}`. - -And lastly, to mirror a display we can use: `xrandr --output {DISPLAYNAME} --same-as {ANOTHER-DISPLAY}` - -So with my desire to mirror my TV and left displays, my Xsetup script now looks like this: - -```bash -#!/bin/sh -# Xsetup - run as root before the login dialog appears - -if [ -e /sbin/prime-offload ]; then -   echo running NVIDIA Prime setup /sbin/prime-offload -   /sbin/prime-offload -fi - -xrandr --output DisplayPort-0 --off -xrandr --output DisplayPort-2 --off -xrandr --output DisplayPort-1 --auto --primary -xrandr --output HDMI-A-0 --mode 1920x1080 -xrandr --output HDMI-A-0 --same-as DisplayPort-1 -``` - -Save this file, reboot, and you should see your login screen now respects these settings. Make sure when you log -back in, you select a Wayland session (if that is your preferred session manager). - -#### Next Steps - -Congratulations! You now have Sunshine starting on boot, you can login to your session remotely, you get all the -benefits of the graphical session permissions, and you can safely shut down your PC with the confidence you can -turn it back on when needed. - -@seealso{As Eric Dong recommended, I'll also send you to automate changing your displays. -You can add multiple commands, to turn off, or configure as many displays as you'd like with the sunshine prep -commands. See [Changing Resolution and Refresh Rate](md_docs_2app__examples#changing-resolution-and-refresh-rate) -for more information and remember that the display names for your prep commands, may be different than what you -found for SDDM.} - - -## macOS -@todo{It's looking lonely here.} - - -## Windows - -| Author | [BeeLeDev](https://github.com/BeeLeDev) | -|------------|-----------------------------------------| -| Difficulty | Intermediate | - -### Discord call cancellation -Cancel Discord call audio with Voicemeeter (Standard) - -#### Voicemeeter Configuration -1. Click "Hardware Out" -2. Set the physical device you receive audio to as your Hardware Out with MME -3. Turn on BUS A for the Virtual Input - -#### Windows Configuration -1. Open the sound settings -2. Set your default Playback as Voicemeeter Input - -@tip{Run audio in the background to find the device that your Virtual Input is using -(Voicemeeter In #), you will see the bar to the right of the device have green bars -going up and down. This device will be referred to as Voicemeeter Input.} - -#### Discord Configuration -1. Open the settings -2. Go to Voice & Video -3. Set your Output Device as the physical device you receive audio to - -@tip{It is usually the same device you set for Hardware Out in Voicemeeter.} - -#### Sunshine Configuration -1. Go to Configuration -2. Go to the Audio/Video tab -3. Set Virtual Sink as Voicemeeter Input - -@note{This should be the device you set as default previously in Playback.} +@admonition{Community | A collection of guides written by the community is available on our +[blog](https://lizardbyte.com/blog). +Feel free to contribute your own tips and trips by making a PR to +[LizardByte.github.io](https://github.com/LizardByte/LizardByte.github.io).}
diff --git a/docs/images/discord_calls_01.png b/docs/images/discord_calls_01.png deleted file mode 100644 index d26e62a811eeda20da7c97c176b9a33b764b3ef4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28055 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 diff --git a/docs/images/discord_calls_02.png b/docs/images/discord_calls_02.png deleted file mode 100644 index 6a739be788b824d669cb07014b97bd9d01c467c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39673 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 diff --git a/docs/images/discord_calls_03.png b/docs/images/discord_calls_03.png deleted file mode 100644 index 0dd345002335a336add6a610b7715742ffc0fce0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28099 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
    bDuA<9bh1@^u1Qv_v3nwgM{ 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

    9-JF4k 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 KpUXO@geCx!?BPfN literal 20640 zcmZ5nRa{h08(vaa>29Q?Q@XpQyIVR0X;`|uOF&9eLZo5I1w^E!8Fffeb&267Yu@qSI1Li)3f9-=8zEzyrsxq!s{-J zz{bN%dkh<`6S_vg@9zBXk513ldxPvHyVlM&db>HV>#hrpg{g32hqgWce@JPIxw52A zdE2}wy~;g}gmy4yFgLS_jYZbY3jQU zn5gLqdN!sR`jObQdpp_?phtI{T1chVPf(u$@O39RX|yQ~xPwh|t@J3vaV53yi24<< z!rFTEnMF_OFHu-H0!3nnPe;9ADPzri4#slg|>5|xTD+BBs3jv`qqWNqemP%EWSkQcd!O-E5< zB+F7j>qP zCX%KcQCs00!1Fm093=7CAa&{67|K~y=}MqC{*9{#RYRF9iD0O;#xz_UZ>z1k<>(ix zB}=qlEyrWJAd`!FAZ~F2J|b`Y#?p7WQSDk^v**HO+(S*8h`gs5o+? z4LP$zbWLwpT5(K{_4n$b&W20#woUm+rKT)uF)~aF_>8=4z#BY%>&GHrEM14=FIAW` zOBH$QkGFaOO=!m7b;C#Z_M!oOni9S{+GR58qlHp*1faxFJmp+Jn!4I_3go;3HLc4q!v z3Z(uKGg{5oRKbzyLBiIaj8;@@($AvYUzxFW@r?oZ%acuksSd18UX<=HA7g3h@xYHY55> zeI88^`!NK>7A3*56vDbHAb=@@Xa9yA)0pB4h&4o>46aL&pcxt~x77dlJBKDlunwaL z+0lM_uIzXPxeee@J3VA{*Y{Y<@9=)%>*>t~M@)BNR zITd4A;RCo&wRu4w}^$F z6B4ZGg7cPh7N1>znBqN3Ne4mRkaf(BBIs;b6L9eZDt!(iz&=}O8{BG)Nq z+iRSK1&ion=35x(_4=cf_Qk)tjil4pm)7QL9ym&d>iq);vD1iSdr6 z`+Kv6%7j3Up93DH8&b4|PLQS61ww`NMx+bMT_<9}I%V+d3l?FQ6|)%5=78W5THqAJwH(82)nU`Tiw1=n=sT#de zTi-yILuucol8^bzh@p8%0xknpuu98puDde#d

    Ham7aEv9?}_6 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 - https://app.lizardbyte.dev/Sunshine/assets/images/AdobeStock_305732536_1920x1280.jpg + https://app.lizardbyte.dev/Sunshine/assets/img/banners/AdobeStock_305732536_1920x1280.jpg Sunshine diff --git a/src/display_device.cpp b/src/display_device.cpp index a9a21b6a..6c2a869f 100644 --- a/src/display_device.cpp +++ b/src/display_device.cpp @@ -119,7 +119,7 @@ namespace display_device { }; /** - * @breif Convert string to unsigned int. + * @brief Convert string to unsigned int. * @note For random reason there is std::stoi, but not std::stou... * @param value String to be converted * @return Parsed unsigned integer. From 1b94e9339ac3d316283168e6d93445fa53c42bc6 Mon Sep 17 00:00:00 2001 From: Lukas Senionis Date: Sun, 12 Jan 2025 21:14:20 +0200 Subject: [PATCH 15/20] feat(display): add display mode remapping option (#3529) Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> --- docs/configuration.md | 86 +++++++ src/config.cpp | 34 +++ src/config.h | 14 ++ src/display_device.cpp | 204 +++++++++++++++- src_assets/common/assets/web/config.html | 35 +-- .../assets/web/configs/tabs/AudioVideo.vue | 2 - .../assets/web/configs/tabs/General.vue | 13 +- .../tabs/audiovideo/DisplayDeviceOptions.vue | 108 ++++++++- .../tabs/audiovideo/DisplayModesSettings.vue | 5 - .../assets/web/public/assets/locale/en.json | 13 + tests/unit/test_display_device.cpp | 222 ++++++++++++++++++ 11 files changed, 701 insertions(+), 35 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 57bb6d9f..fb36e159 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1234,6 +1234,92 @@ editing the `conf` file in a text editor. Use the examples as reference. +### dd_mode_remapping + + + + + + + + + + + + + + +
    Description + Remap the requested resolution and FPS to another display mode.
    + Depending on the [dd_resolution_option](#dd_resolution_option) and + [dd_refresh_rate_option](#dd_refresh_rate_option) values, the following mapping + groups are available: +
      +
    • `mixed` - both options are set to `auto`.
    • +
    • + `resolution_only` - only [dd_resolution_option](#dd_resolution_option) is set to `auto`. +
    • +
    • + `refresh_rate_only` - only [dd_refresh_rate_option](#dd_refresh_rate_option) is set to `auto`. +
    • +
    + For each of those groups, a list of fields can be configured to perform remapping: +
      +
    • + `requested_resolution` - resolution that needs to be matched in order to use this remapping entry. +
    • +
    • `requested_fps` - FPS that needs to be matched in order to use this remapping entry.
    • +
    • `final_resolution` - resolution value to be used if the entry was matched.
    • +
    • `final_refresh_rate` - refresh rate value to be used if the entry was matched.
    • +
    + If `requested_*` field is left empty, it will match everything.
    + If `final_*` field is left empty, the original value will not be remapped and either a requested, manual + or current value is used. However, at least one `final_*` must be set, otherwise the entry is considered + invalid.
    + @note{"Optimize game settings" must be enabled on client side for ANY entry with `resolution` + field to be considered.} + @note{First entry to be matched in the list is the one that will be used.} + @tip{`requested_resolution` and `final_resolution` can be omitted for `refresh_rate_only` group.} + @tip{`requested_fps` and `final_refresh_rate` can be omitted for `resolution_only` group.} + @note{Applies to Windows only.} +
    Default@code{} + dd_mode_remapping = { + "mixed": [], + "resolution_only": [], + "refresh_rate_only": [] + } + @endcode +
    Example@code{} + dd_mode_remapping = { + "mixed": [ + { + "requested_fps": "60", + "final_refresh_rate": "119.95", + "requested_resolution": "1920x1080", + "final_resolution": "2560x1440" + }, + { + "requested_fps": "60", + "final_refresh_rate": "120", + "requested_resolution": "", + "final_resolution": "" + } + ], + "resolution_only": [ + { + "requested_resolution": "1920x1080", + "final_resolution": "2560x1440" + } + ], + "refresh_rate_only": [ + { + "requested_fps": "60", + "final_refresh_rate": "119.95" + } + ] + }@endcode +
    + ### min_fps_factor diff --git a/src/config.cpp b/src/config.cpp index cf2b90a2..25a2f51e 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -379,6 +379,38 @@ namespace config { #undef _CONVERT_2_ARG_ return video_t::dd_t::hdr_option_e::disabled; // Default to this if value is invalid } + + video_t::dd_t::mode_remapping_t + mode_remapping_from_view(const std::string_view value) { + const auto parse_entry_list { [](const auto &entry_list, auto &output_field) { + for (auto &[_, entry] : entry_list) { + auto requested_resolution = entry.template get_optional("requested_resolution"s); + auto requested_fps = entry.template get_optional("requested_fps"s); + auto final_resolution = entry.template get_optional("final_resolution"s); + auto final_refresh_rate = entry.template get_optional("final_refresh_rate"s); + + output_field.push_back(video_t::dd_t::mode_remapping_entry_t { + requested_resolution.value_or(""), + requested_fps.value_or(""), + final_resolution.value_or(""), + final_refresh_rate.value_or("") }); + } + } }; + + // We need to add a wrapping object to make it valid JSON, otherwise ptree cannot parse it. + std::stringstream json_stream; + json_stream << "{\"dd_mode_remapping\":" << value << "}"; + + boost::property_tree::ptree json_tree; + boost::property_tree::read_json(json_stream, json_tree); + + video_t::dd_t::mode_remapping_t output; + parse_entry_list(json_tree.get_child("dd_mode_remapping.mixed"), output.mixed); + parse_entry_list(json_tree.get_child("dd_mode_remapping.resolution_only"), output.resolution_only); + parse_entry_list(json_tree.get_child("dd_mode_remapping.refresh_rate_only"), output.refresh_rate_only); + + return output; + } } // namespace dd video_t video { @@ -447,6 +479,7 @@ namespace config { {}, // manual_refresh_rate video_t::dd_t::hdr_option_e::automatic, // hdr_option 3s, // config_revert_delay + {}, // mode_remapping {} // wa }, // display_device @@ -1105,6 +1138,7 @@ namespace config { video.dd.config_revert_delay = std::chrono::milliseconds { value }; } } + generic_f(vars, "dd_mode_remapping", video.dd.mode_remapping, dd::mode_remapping_from_view); 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 }); diff --git a/src/config.h b/src/config.h index 7e1813e6..c8fea38c 100644 --- a/src/config.h +++ b/src/config.h @@ -110,6 +110,19 @@ namespace config { automatic ///< Change HDR settings and use the state requested by Moonlight. }; + struct mode_remapping_entry_t { + std::string requested_resolution; + std::string requested_fps; + std::string final_resolution; + std::string final_refresh_rate; + }; + + struct mode_remapping_t { + std::vector mixed; ///< To be used when `resolution_option` and `refresh_rate_option` is set to `automatic`. + std::vector resolution_only; ///< To be use when only `resolution_option` is set to `automatic`. + std::vector refresh_rate_only; ///< To be use when only `refresh_rate_option` is set to `automatic`. + }; + config_option_e configuration_option; resolution_option_e resolution_option; std::string manual_resolution; ///< Manual resolution in case `resolution_option == resolution_option_e::manual`. @@ -117,6 +130,7 @@ namespace config { std::string manual_refresh_rate; ///< Manual refresh rate in case `refresh_rate_option == refresh_rate_option_e::manual`. hdr_option_e hdr_option; std::chrono::milliseconds config_revert_delay; ///< Time to wait until settings are reverted (after stream ends/app exists). + mode_remapping_t mode_remapping; workarounds_t wa; } dd; diff --git a/src/display_device.cpp b/src/display_device.cpp index 6c2a869f..e337e9a8 100644 --- a/src/display_device.cpp +++ b/src/display_device.cpp @@ -188,6 +188,7 @@ namespace display_device { * @brief Parse refresh rate value from the string. * @param input String to be parsed. * @param output Reference to output variable to fill in. + * @param allow_decimal_point Specify whether the decimal point is allowed or not. * @returns True on successful parsing (empty string allowed), false otherwise. * * @examples @@ -203,10 +204,10 @@ namespace display_device { * @examples_end */ bool - parse_refresh_rate_string(const std::string &input, std::optional &output) { + parse_refresh_rate_string(const std::string &input, std::optional &output, const bool allow_decimal_point = true) { static const auto is_zero { [](const auto &character) { return character == '0'; } }; const std::string trimmed_input { boost::algorithm::trim_copy(input) }; - const std::regex refresh_rate_regex { R"(^(\d+)(?:\.(\d+))?$)" }; + const std::regex refresh_rate_regex { allow_decimal_point ? R"(^(\d+)(?:\.(\d+))?$)" : R"(^(\d+)$)" }; if (std::smatch match; std::regex_match(trimmed_input, match, refresh_rate_regex)) { try { @@ -217,7 +218,7 @@ namespace display_device { } std::string trimmed_match_2; - if (match[2].matched) { + if (allow_decimal_point && match[2].matched) { trimmed_match_2 = boost::algorithm::trim_right_copy_if(match[2].str(), is_zero); } @@ -261,7 +262,7 @@ namespace display_device { return true; } - BOOST_LOG(error) << "Failed to parse refresh rate string " << trimmed_input << R"(. Must have a pattern of "123" or "123.456"!)"; + BOOST_LOG(error) << "Failed to parse refresh rate string " << trimmed_input << ". Must have a pattern of " << (allow_decimal_point ? R"("123" or "123.456")" : R"("123")") << "!"; } return false; @@ -435,6 +436,196 @@ namespace display_device { return std::nullopt; } + /** + * @brief Indicates which remapping fields and config structure shall be used. + */ + enum class remapping_type_e { + mixed, ///! Both reseolution and refresh rate may be remapped + resolution_only, ///! Only resolution will be remapped + refresh_rate_only ///! Only refresh rate will be remapped + }; + + /** + * @brief Determine the ramapping type from the user config. + * @param video_config User's video related configuration. + * @returns Enum value if remapping can be performed, null optional if remapping shall be skipped. + */ + std::optional + determine_remapping_type(const config::video_t &video_config) { + using dd_t = config::video_t::dd_t; + const bool auto_resolution { video_config.dd.resolution_option == dd_t::resolution_option_e::automatic }; + const bool auto_refresh_rate { video_config.dd.refresh_rate_option == dd_t::refresh_rate_option_e::automatic }; + + if (auto_resolution && auto_refresh_rate) { + return remapping_type_e::mixed; + } + + if (auto_resolution) { + return remapping_type_e::resolution_only; + } + + if (auto_refresh_rate) { + return remapping_type_e::refresh_rate_only; + } + + return std::nullopt; + } + + /** + * @brief Contains remapping data parsed from the string values. + */ + struct parsed_remapping_entry_t { + std::optional requested_resolution; + std::optional requested_fps; + std::optional final_resolution; + std::optional final_refresh_rate; + }; + + /** + * @brief Check if resolution is to be mapped based on remmaping type. + * @param type Remapping type to check. + * @returns True if resolution is to be mapped, false otherwise. + */ + bool + is_resolution_mapped(const remapping_type_e type) { + return type == remapping_type_e::resolution_only || type == remapping_type_e::mixed; + } + + /** + * @brief Check if FPS is to be mapped based on remmaping type. + * @param type Remapping type to check. + * @returns True if FPS is to be mapped, false otherwise. + */ + bool + is_fps_mapped(const remapping_type_e type) { + return type == remapping_type_e::refresh_rate_only || type == remapping_type_e::mixed; + } + + /** + * @brief Parse the remapping entry from the config into an internal structure. + * @param entry Entry to parse. + * @param type Specify which entry fields should be parsed. + * @returns Parsed structure or null optional if a necessary field could not be parsed. + */ + std::optional + parse_remapping_entry(const config::video_t::dd_t::mode_remapping_entry_t &entry, const remapping_type_e type) { + parsed_remapping_entry_t result {}; + + if (is_resolution_mapped(type) && (!parse_resolution_string(entry.requested_resolution, result.requested_resolution) || + !parse_resolution_string(entry.final_resolution, result.final_resolution))) { + return std::nullopt; + } + + if (is_fps_mapped(type) && (!parse_refresh_rate_string(entry.requested_fps, result.requested_fps, false) || + !parse_refresh_rate_string(entry.final_refresh_rate, result.final_refresh_rate))) { + return std::nullopt; + } + + return result; + } + + /** + * @brief Remap the the requested display mode based on the config. + * @param video_config User's video related configuration. + * @param session Session information. + * @param config A reference to a config object that will be modified on success. + * @returns True if the remapping was performed or skipped, false if remapping has failed due to invalid config. + * + * @examples + * const std::shared_ptr launch_session; + * const config::video_t &video_config { config::video }; + * + * SingleDisplayConfiguration config; + * const bool success = remap_display_mode_if_needed(video_config, *launch_session, config); + * @examples_end + */ + bool + remap_display_mode_if_needed(const config::video_t &video_config, const rtsp_stream::launch_session_t &session, SingleDisplayConfiguration &config) { + const auto remapping_type { determine_remapping_type(video_config) }; + if (!remapping_type) { + return true; + } + + const auto &remapping_list { [&]() { + using enum remapping_type_e; + + switch (*remapping_type) { + case resolution_only: + return video_config.dd.mode_remapping.resolution_only; + case refresh_rate_only: + return video_config.dd.mode_remapping.refresh_rate_only; + case mixed: + default: + return video_config.dd.mode_remapping.mixed; + } + }() }; + + if (remapping_list.empty()) { + BOOST_LOG(debug) << "No values are available for display mode remapping."; + return true; + } + BOOST_LOG(debug) << "Trying to remap display modes..."; + + const auto entry_to_string { [type = *remapping_type](const config::video_t::dd_t::mode_remapping_entry_t &entry) { + const bool mapping_resolution { is_resolution_mapped(type) }; + const bool mapping_fps { is_fps_mapped(type) }; + + // clang-format off + return (mapping_resolution ? " - requested resolution: "s + entry.requested_resolution + "\n" : "") + + (mapping_fps ? " - requested FPS: "s + entry.requested_fps + "\n" : "") + + (mapping_resolution ? " - final resolution: "s + entry.final_resolution + "\n" : "") + + (mapping_fps ? " - final refresh rate: "s + entry.final_refresh_rate : ""); + // clang-format on + } }; + + for (const auto &entry : remapping_list) { + const auto parsed_entry { parse_remapping_entry(entry, *remapping_type) }; + if (!parsed_entry) { + BOOST_LOG(error) << "Failed to parse remapping entry from:\n" + << entry_to_string(entry); + return false; + } + + if (!parsed_entry->final_resolution && !parsed_entry->final_refresh_rate) { + BOOST_LOG(error) << "At least one final value must be set for remapping display modes! Entry:\n" + << entry_to_string(entry); + return false; + } + + if (!session.enable_sops && (parsed_entry->requested_resolution || parsed_entry->final_resolution)) { + BOOST_LOG(warning) << R"(Skipping remapping entry, because the "Optimize game settings" is not set in the client! Entry:\n)" + << entry_to_string(entry); + continue; + } + + // Note: at this point config should already have parsed resolution set. + if (parsed_entry->requested_resolution && parsed_entry->requested_resolution != config.m_resolution) { + BOOST_LOG(verbose) << "Skipping remapping because requested resolutions do not match! Entry:\n" + << entry_to_string(entry); + continue; + } + + // Note: at this point config should already have parsed refresh rate set. + if (parsed_entry->requested_fps && parsed_entry->requested_fps != config.m_refresh_rate) { + BOOST_LOG(verbose) << "Skipping remapping because requested FPS do not match! Entry:\n" + << entry_to_string(entry); + continue; + } + + BOOST_LOG(info) << "Remapping requested display mode. Entry:\n" + << entry_to_string(entry); + if (parsed_entry->final_resolution) { + config.m_resolution = parsed_entry->final_resolution; + } + if (parsed_entry->final_refresh_rate) { + config.m_refresh_rate = parsed_entry->final_refresh_rate; + } + break; + } + + return true; + } + /** * @brief Construct a settings manager interface to manage display device settings. * @param persistence_filepath File location for saving persistent state. @@ -626,6 +817,11 @@ namespace display_device { return failed_to_parse_tag_t {}; } + if (!remap_display_mode_if_needed(video_config, session, config)) { + // Error already logged + return failed_to_parse_tag_t {}; + } + return config; } } // namespace display_device diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 3eec36d7..e6f4f2f7 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -33,7 +33,6 @@ @@ -127,7 +126,6 @@ restarted: false, config: null, currentTab: "general", - global_prep_cmd: [], tabs: [ // TODO: Move the options to each Component instead, encapsulate. { id: "general", @@ -136,7 +134,7 @@ "locale": "en", "sunshine_name": "", "min_log_level": 2, - "global_prep_cmd": "[]", + "global_prep_cmd": [], "notify_pre_releases": "disabled", }, }, @@ -178,6 +176,7 @@ "dd_manual_refresh_rate": "", "dd_hdr_option": "auto", "dd_config_revert_delay": 3000, + "dd_mode_remapping": {"mixed": [], "resolution_only": [], "refresh_rate_only": []}, "dd_wa_hdr_toggle": "disabled", "min_fps_factor": 1, }, @@ -320,17 +319,23 @@ // TODO: let each tab's Component handle it's own data instead of doing it here + // Parse the special options before population if available + const specialOptions = ["dd_mode_remapping", "global_prep_cmd"] + for (const optionKey of specialOptions) { + if (this.config.hasOwnProperty(optionKey)) { + this.config[optionKey] = JSON.parse(this.config[optionKey]); + } + } + // Populate default values from tabs options this.tabs.forEach(tab => { Object.keys(tab.options).forEach(optionKey => { if (this.config[optionKey] === undefined) { - this.config[optionKey] = tab.options[optionKey]; + // Make sure to copy by value + this.config[optionKey] = JSON.parse(JSON.stringify(tab.options[optionKey])); } }); }); - - this.config.global_prep_cmd = this.config.global_prep_cmd || []; - this.global_prep_cmd = JSON.parse(this.config.global_prep_cmd); }); }, methods: { @@ -338,26 +343,26 @@ this.$forceUpdate() }, serialize() { - this.config.global_prep_cmd = JSON.stringify(this.global_prep_cmd); + let config = JSON.parse(JSON.stringify(this.config)); + config.global_prep_cmd = JSON.stringify(config.global_prep_cmd); + config.dd_mode_remapping = JSON.stringify(config.dd_mode_remapping); + return config; }, save() { this.saved = false; this.restarted = false; - this.serialize(); // create a temp copy of this.config to use for the post request - let config = JSON.parse(JSON.stringify(this.config)) + let config = this.serialize(); // delete default values from this.config this.tabs.forEach(tab => { Object.keys(tab.options).forEach(optionKey => { let delete_value = false - if (["global_prep_cmd"].includes(optionKey)) { - let config_value, default_value - - config_value = JSON.parse(config[optionKey]) - default_value = JSON.parse(tab.options[optionKey]) + if (["global_prep_cmd", "dd_mode_remapping"].includes(optionKey)) { + const config_value = config[optionKey] + const default_value = JSON.stringify(tab.options[optionKey]) if (config_value === default_value) { delete_value = true diff --git a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue index dfcc1c4e..cfd7e373 100644 --- a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue +++ b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue @@ -11,7 +11,6 @@ import Checkbox from "../../Checkbox.vue"; const props = defineProps([ 'platform', 'config', - 'min_fps_factor', ]) const config = ref(props.config) @@ -95,7 +94,6 @@ const config = ref(props.config) diff --git a/src_assets/common/assets/web/configs/tabs/General.vue b/src_assets/common/assets/web/configs/tabs/General.vue index 74f43938..d2dc7392 100644 --- a/src_assets/common/assets/web/configs/tabs/General.vue +++ b/src_assets/common/assets/web/configs/tabs/General.vue @@ -4,12 +4,9 @@ import { ref } from 'vue' const props = defineProps({ platform: String, - config: Object, - globalPrepCmd: Array + config: Object }) - const config = ref(props.config) -const globalPrepCmd = ref(props.globalPrepCmd) function addCmd() { let template = { @@ -20,11 +17,11 @@ function addCmd() { if (props.platform === 'windows') { template = { ...template, elevated: false }; } - globalPrepCmd.value.push(template); + config.value.global_prep_cmd.push(template); } function removeCmd(index) { - globalPrepCmd.value.splice(index,1) + config.value.global_prep_cmd.splice(index,1) } @@ -83,7 +80,7 @@ function removeCmd(index) {
    {{ $t('config.global_prep_cmd_desc') }}
    -
    +
    @@ -95,7 +92,7 @@ function removeCmd(index) { - + diff --git a/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayDeviceOptions.vue b/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayDeviceOptions.vue index 379d1344..ee295173 100644 --- a/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayDeviceOptions.vue +++ b/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayDeviceOptions.vue @@ -7,8 +7,44 @@ const props = defineProps({ platform: String, config: Object }) - const config = ref(props.config) + +const REFRESH_RATE_ONLY = "refresh_rate_only" +const RESOLUTION_ONLY = "resolution_only" +const MIXED = "mixed" + +function canBeRemapped() { + return (config.value.dd_resolution_option === "auto" || config.value.dd_refresh_rate_option === "auto") + && config.value.dd_configuration_option !== "disabled"; +} + +function getRemappingType() { + // Assuming here that at least one setting is set to "auto" if other is not + if (config.value.dd_resolution_option !== "auto") { + return REFRESH_RATE_ONLY; + } + if (config.value.dd_refresh_rate_option !== "auto") { + return RESOLUTION_ONLY; + } + return MIXED; +} + +function addRemappingEntry() { + const type = getRemappingType(); + let template = {}; + + if (type !== RESOLUTION_ONLY) { + template["requested_fps"] = ""; + template["final_refresh_rate"] = ""; + } + + if (type !== REFRESH_RATE_ONLY) { + template["requested_resolution"] = ""; + template["final_resolution"] = ""; + } + + config.value.dd_mode_remapping[type].push(template); +}
    {{ $t('_common.do_cmd') }}

    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