Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f4738b262 | |||
| 31a8438c8f | |||
| a076ac9db4 | |||
| 911e3ab50e | |||
| d8f4a7f883 | |||
| 59432da5bf | |||
| 4606b368a0 | |||
| 80167a7f3f | |||
| 91cb80bbe2 | |||
| df1c7ca9d2 | |||
| 0f96db8d29 | |||
| 8042f1b9f6 | |||
| 757eb1f655 | |||
| 49fb694aaf | |||
| 508e290a24 | |||
| 413996f9f3 | |||
| cda4e000ee | |||
| 2e9f62d415 | |||
| 4ef821adc9 | |||
| 21f4eacefb | |||
| 72738a256b | |||
| a54c146772 | |||
| 43a4cdfe45 | |||
| 39d481467a | |||
| 3afcf1c12e | |||
| da140848ba | |||
| 288853a6cc | |||
| 365e251408 | |||
| eaa5eccb98 | |||
| eabeae00d6 | |||
| d209d6c336 | |||
| eff5d5728f | |||
| 2ee7a904d6 | |||
| f477870f74 | |||
| 2ccb3d469f | |||
| c63b39ff01 | |||
| 4bb01146e6 | |||
| 35a476f6ab | |||
| 1744ae1dcc | |||
| 7a38bb22f1 | |||
| b325bf2a39 | |||
| 52d1049c98 | |||
| 7caca61c1e | |||
| 3f5b5ca4a0 | |||
| 29e5b1946d | |||
| 09ffdcae32 | |||
| 7fc6ffdda9 | |||
| 0085d1e52e | |||
| ccd36913de | |||
| 4dcc4e92a2 | |||
| a4dab93c36 | |||
| 39adbc42f0 | |||
| 71da1d9484 | |||
| 989b045be7 | |||
| bbe708b767 | |||
| b6513fb096 | |||
| a4d9069a3d | |||
| be49f1e02b | |||
| 0417f3d4fa | |||
| 3feca3769a |
@@ -20,6 +20,9 @@ jobs:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: update apt-get
|
||||
run: sudo apt-get update
|
||||
|
||||
- name: Install rpm/flatpak packages
|
||||
run: sudo apt-get install -y flatpak flatpak-builder rpm libarchive-tools
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
----------------------------------------
|
||||
BADGES
|
||||
@@ -58,7 +57,7 @@
|
||||
src="https://img.shields.io/badge/-DISCORD-5865f2?style=for-the-badge&logo=discord&logoColor=ffffff"
|
||||
alt="discord" /></a>
|
||||
<a href="https://twitter.com/BSManager_"><img
|
||||
src="https://img.shields.io/badge/-Twitter-black?style=for-the-badge&logo=X" alt="Twitter" /></a>
|
||||
src="https://img.shields.io/badge/-Twitter-black?style=for-the-badge&logo=X" alt="Twitter" /></a>
|
||||
<a href="https://www.bsmanager.io">
|
||||
<img src="https://img.shields.io/badge/-WebSite-00649c?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAwIDEwMDAiPgogIDxnIGZpbGw9IiMzYjgyZmYiPgogICAgPHBhdGggZD0ibTYyNi43NSA0ODUuOTEgMjAuODIgMzUxLjk0YTM1LjIxIDM1LjIxIDAgMCAxLTE1LjQ5IDMxLjM2bC0xNDQuNzEgOTYuMTRoLS4wNmEzNS41NiAzNS41NiAwIDAgMS0xMS4zMSA0Ljc3IDM0Ljg1IDM0Ljg1IDAgMCAxLTIzLjUxLTIuODdMMTM2LjU4IDgwOS45YTExNS40NSAxMTUuNDUgMCAwIDEtNjMuNzUtOTYuNDlMNTIgMzYxLjQ2YTM1LjI2IDM1LjI2IDAgMCAxIDE1LjIzLTMxLjIxbDE0NS4wNS05Ni4zNmEzNS43MiAzNS43MiAwIDAgMSAxMS4yNy00LjcgMzQuODcgMzQuODcgMCAwIDEgMjMuNTEgMi44N0w1NjMgMzg5LjQyYTExNS40MSAxMTUuNDEgMCAwIDEgNjMuNzUgOTYuNDlaIi8+CiAgICA8cGF0aCBmaWx0ZXI9ImJyaWdodG5lc3MoMzAlKSIgZD0ibTYxMy43OCA0ODYuNjcgMjAuODIgMzUyYTIyLjM0IDIyLjM0IDAgMCAxLTMyLjI2IDIxLjMxTDI4Ni40IDcwMi41N0ExMDIuNDMgMTAyLjQzIDAgMCAxIDIyOS44MyA2MTdMMjA5IDI2NWEyMi4zNSAyMi4zNSAwIDAgMSAzMi4yNi0yMS4zMkw1NTcuMiA0MDEuMDVhMTAyLjQyIDEwMi40MiAwIDAgMSA1Ni41OCA4NS42MloiLz4KICA8L2c+CiAgPHBhdGggZD0iTTcxOC4yOSA3NzYuNDggNzAwIDgwNC4xOGMtNDAuNjMgMzkuNS04Ny4wNiA2Ny4yNS0xMzMuODEgOTAuNzUtODcuMzIgNDMuOS0yMDMuNjcgNzcuMTktMjQ5IDkwLjM0YTQ4IDQ4IDAgMCAxLTI2LjE3LjE2Yy02NC0xNy43Mi0xODguNjItNzItMjM2LjI4LTEyNS40OWE0LjIzIDQuMjMgMCAwIDEgNS4zLTYuNDVjMzUuNjQgMjAuODcgMTExLjIyIDU3LjY0IDE1Ni4zNyA3NC4yMiA0OS4xOCAxOC4wNiA4MS4yOSA4Ljc0IDkyLjI4LTMuMjZhNC43NyA0Ljc3IDAgMCAxIDcuMjQuMjFjMTkuNTEgMjQgMTA3LjI2IDQuNiAxNjYuNjQtMTMgNjQuMzQtMTkgMTg1Ljg1LTc2LjU2IDIzNS43Mi0xMzUuMThabS0xOTYuMiAxNTcuMzMtMTQ3LjQ1IDU1LjY4YTMuOTIgMy45MiAwIDAgMCAxLjQ0IDcuNThsNTQuNjItLjc3YTM1LjYzIDM1LjYzIDAgMCAwIDE5LTUuNzlsNzYtNDkuNTVhNCA0IDAgMCAwLTMuNjEtNy4xNVpNMjE4LjkyIDE5NS41NmMtMjEuMTQuMDctMzMuNTUgMzUuODYtMzMuMTYgODkuMDguMiAyOCAyLjU4IDkyLjc0IDQuNzkgMTQ3LjczLjIzIDUuNzEtOC4xOCA2LjQ4LTkgLjgxbC0zOC40Mi0yNzAuNzFhNC4zOCA0LjM4IDAgMCAxIDcuMTItNCA2My42MyA2My42MyAwIDAgMCAyMS45MyAxMS44NSA0LjYxIDQuNjEgMCAwIDAgNS45LTMuNzJjNC4yLTI3LjcgMTYuNjMtNTguNzQgMzcuMzUtODguNzRhNC4zNiA0LjM2IDAgMCAxIDcuNTYuNjljMTAuODYgMjMuODcgNDkgMTAzLjggOTMuMzQgMTUyLjc5YTQuNjUgNC42NSAwIDAgMS01LjYyIDcuMjJjLTM2LjU3LTE5LjM4LTgzLjAxLTQzLjAyLTkxLjc5LTQzWm0tMTUuNDcgMzg0LjhhNC41MSA0LjUxIDAgMCAxLTQuNDYtMy45MmwtMTMuODItMTA3LjI5YTQuNSA0LjUgMCAwIDEgOC45My0xLjE1bDEzLjgyIDEwNy4yOWE0LjUgNC41IDAgMCAxLTMuODkgNSAzLjg2IDMuODYgMCAwIDEtLjU4LjA3Wk01NzcuMDggNTQuNThjLTQ2Ljc5IDE1Ljg1LTExNS40NSA0MC41Ni0xNTguNzMgNjIuNzlhMzUuNSAzNS41IDAgMCAwLTE4LjU3IDI0LjUzYy0zLjk1IDE5LjQ5LTExIDU2LjA5LTE5IDEwNy4yNi0uNyA0LjQ4LTcuMzYgMy43OC03LjExLS43NWwxMC42Ni0xOTQuNzNhMy43OCAzLjc4IDAgMCAxIDcuNDYtLjY2bDMuNDkgMTQuODVhMi41NyAyLjU3IDAgMCAwIDQuNjkuNzdjNi4xOC0xMCAyMi4zNy0zMi43OSA1Ny4zMi02NWE0LjEzIDQuMTMgMCAwIDEgNi41NiA0LjcybC0zMi41NCA3Mi44OXM3OC43OC0xOS4zNyAxNDQtMzMuMDljNC4xMS0uODcgNS42OSA1LjA4IDEuNzcgNi40MloiIGZpbGw9IiNmZmYiIHN0cm9rZT0iYmxhY2siLz4KICA8ZyBmaWxsPSIjZjQ0Ij4KICAgIDxwYXRoIGZpbHRlcj0iYnJpZ2h0bmVzcyg2MCUpIHNhdHVyYXRlKDEyMCUpIiBkPSJtOTQ3LjA3IDQxMC4zNS01Ny43NSAzNDcuNzlhMzUuMTggMzUuMTggMCAwIDEtMjIuMDYgMjcuMTVsLTE2Mi40MyA2MS42NWgtLjA2YTM1LjUyIDM1LjUyIDAgMCAxLTEyLjA2IDIuMTEgMzQuODMgMzQuODMgMCAwIDEtMjIuMjktOGwtMjczLjE3LTIyMy41YTExNS40MyAxMTUuNDMgMCAwIDEtNDAuNzYtMTA4LjIybDU3Ljc1LTM0Ny44QTM1LjI1IDM1LjI1IDAgMCAxIDQzNiAxMzQuNDdsMTYyLjgzLTYxLjc5YTM1LjUxIDM1LjUxIDAgMCAxIDEyLTIuMDggMzQuODMgMzQuODMgMCAwIDEgMjIuMjkgOGwyNzMuMTkgMjIzLjUyYTExNS4zOCAxMTUuMzggMCAwIDEgNDAuNzYgMTA4LjIzWiIvPgogICAgPHBhdGggZD0iTTkzNC4yNSA0MDguMjIgODc2LjUgNzU2YTIyLjM0IDIyLjM0IDAgMCAxLTM2LjE4IDEzLjYzbC0yNzMuMTctMjIzLjVhMTAyLjQyIDEwMi40MiAwIDAgMS0zNi4xNy05Nmw1Ny43NS0zNDcuODNhMjIuMzQgMjIuMzQgMCAwIDEgMzYuMTgtMTMuNjNsMjczLjE3IDIyMy41MWExMDIuNDIgMTAyLjQyIDAgMCAxIDM2LjE3IDk2LjA0WiIvPgogIDwvZz4KICA8ZWxsaXBzZSBmaWxsPSIjZmZmIiBzdHJva2U9ImJsYWNrIiBjeD0iNzMyLjYxIiBjeT0iNDI5LjE2IiByeD0iNjIuODMiIHJ5PSIxMTAuNzMiIHRyYW5zZm9ybT0icm90YXRlKC0xNS40NSA3MzIuNzAzIDQyOS4xOTkpIi8+Cjwvc3ZnPg==" alt="Website" /></a>
|
||||
<a href="https://www.patreon.com/bsmanager"><img
|
||||
@@ -72,76 +71,64 @@
|
||||
-->
|
||||
<div>
|
||||
<hr>
|
||||
<details open>
|
||||
<details open>
|
||||
<summary><b>Table of Contents</b></summary>
|
||||
<ul>
|
||||
<li>
|
||||
<li>
|
||||
<details>
|
||||
<summary><a href="#bsmanager-your-beat-saber-assistant">BSManager: Your Beat Saber Assistant</a></summary>
|
||||
<ul>
|
||||
<summary><a href="#bsmanager-your-beat-saber-assistant">BSManager: Your Beat Saber Assistant</a></summary>
|
||||
<ul>
|
||||
<li><a href="#what-is-it">What is it?</a></li>
|
||||
<li><a href="#why-use-it">Why use it?</a></li>
|
||||
<li><a href="#built-with">Built with</a></li>
|
||||
<li><a href="#how-to-install">How to install</a></li>
|
||||
</ul>
|
||||
<li><a href="#how-to-install">How to install?</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
<li>
|
||||
</li>
|
||||
<li>
|
||||
<details>
|
||||
<summary><a href="#version-management">Version Management</a></summary>
|
||||
<ul>
|
||||
<li>
|
||||
<details>
|
||||
<summary><a href="#downloading-multiple-versions">Downloading multiple versions</a></summary>
|
||||
<ul>
|
||||
<li>via Steam account</li>
|
||||
<li>via Oculus (PCVR) account</li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
<li><a href="#multi-version-sharing">Multi-version Sharing</a></li>
|
||||
<li><a href="#importing-an-existing-version-of-beat-saber">Importing an existing version of Beat Saber</a>
|
||||
</li>
|
||||
<li><a href="#launch-arguments">Launch arguments</a></li>
|
||||
<li><a href="#shortcut-creation">Shortcut creation</a></li>
|
||||
</ul>
|
||||
<summary><a href="#version-management">Version Management</a></summary>
|
||||
<ul>
|
||||
<li><a href="#downloading-multiple-versions">Downloading Multiple Versions</a></li>
|
||||
<li><a href="#multi-version-sharing">Multi-Version Sharing</a></li>
|
||||
<li><a href="#importing-an-existing-version-of-beat-saber">Importing an Existing Version of Beat Saber</a></li>
|
||||
<li><a href="#launch-arguments">Launch Arguments</a></li>
|
||||
<li><a href="#shortcut-creation">Shortcut Creation</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
<li>
|
||||
</li>
|
||||
<li>
|
||||
<details>
|
||||
<summary><a href="#download-and-installation">Download and Installation</a></summary>
|
||||
<ul>
|
||||
<li><a href="#maps">Maps</a></li>
|
||||
<li><a href="#playlists">Playlists</a></li>
|
||||
<li><a href="#mods">Mods</a></li>
|
||||
<li><a href="#models">Models</a></li>
|
||||
</ul>
|
||||
<summary><a href="#download-and-installation">Download and Installation</a></summary>
|
||||
<ul>
|
||||
<li><a href="#features">Features</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
<li>
|
||||
</li>
|
||||
<li>
|
||||
<details>
|
||||
<summary><a href="#customization">Customization</a></summary>
|
||||
<ul>
|
||||
<summary><a href="#customization">Customization</a></summary>
|
||||
<ul>
|
||||
<li><a href="#tool">Tool</a></li>
|
||||
<li><a href="#beat-saber-instance">Beat Saber Instance</a></li>
|
||||
</ul>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
<li>
|
||||
</li>
|
||||
<li>
|
||||
<details>
|
||||
<summary><a href="#more-information">More Information</a></summary>
|
||||
<ul>
|
||||
<summary><a href="#more-information">More Information</a></summary>
|
||||
<ul>
|
||||
<li><a href="#support">Support</a></li>
|
||||
<li><a href="#credits">Credits</a></li>
|
||||
<li><a href="#Contributing">Contributing</a></li>
|
||||
</ul>
|
||||
<li><a href="#contributing">Contributing</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
<div align="right">
|
||||
</details>
|
||||
<div align="right">
|
||||
[<a href="#readme-bot">Go to bottom</a>]
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -155,7 +142,6 @@
|
||||
<h1><b>BSManager: Your Beat Saber Assistant</b></h1>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
WHAT IS IT?
|
||||
----------------------------------------
|
||||
@@ -166,7 +152,6 @@
|
||||
Saber</a> experience by centralizing the management of mods, maps, and game versions in one place.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
WHY USE IT?
|
||||
----------------------------------------
|
||||
@@ -189,7 +174,6 @@
|
||||
<video src="https://github.com/Zagrios/bs-manager/assets/40648115/012b0196-74ef-4ee5-86a4-49e835612f96"/>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
BUILT WITH
|
||||
----------------------------------------
|
||||
@@ -219,7 +203,6 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
HOW TO INSTALL
|
||||
----------------------------------------
|
||||
@@ -231,8 +214,12 @@
|
||||
<div>
|
||||
<h3><b>Windows Installation</b></h3>
|
||||
<ul>
|
||||
<li>Download the <a href="https://github.com/Zagrios/bs-manager/releases/latest">latest release</a> from <a
|
||||
href="https://github.com/Zagrios/bs-manager/releases">Releases</a>.</li>
|
||||
<li>Download and install <a href="https://www.bsmanager.io">BSManager</a> from one of the following sources:</li>
|
||||
<ul>
|
||||
<li><a href="https://github.com/Zagrios/bs-manager/releases/latest">GitHub</a></li>
|
||||
<li><a href="https://www.bsmanager.io">Official Website</a></li>
|
||||
<li><a href="https://www.nexusmods.com/beatsaber/mods/18?tab=files">Nexus Mods</a></li>
|
||||
</ul>
|
||||
<li>Execute the installer and <a href="https://github.com/Zagrios/bs-manager">BSManager</a> will start itself.</li>
|
||||
<li>Once it's done, if you want to download a version, select a version and download it! </li>
|
||||
<li>From the version page you can choose options, launch the version, and more!</li>
|
||||
@@ -244,8 +231,8 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3><b>Linux installation</b></h3>
|
||||
<p>Refer to <a href="https://github.com/Zagrios/bs-manager/wiki/Linux">Linux wiki</a></p>
|
||||
<h3><b>Linux Installation</b></h3>
|
||||
<p>BSManager is also available on Linux! Follow the step-by-step guide in our <a href="https://github.com/Zagrios/bs-manager/wiki/Linux">Linux installation wiki</a> to set it up properly.</p>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
@@ -264,48 +251,27 @@
|
||||
existing ones, provided that you own the game on a Steam or Oculus (PC) account.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
DOWNLOADING MULTIPLE VERSIONS
|
||||
----------------------------------------
|
||||
-->
|
||||
<div>
|
||||
<h2>Downloading Multiple Versions</h2>
|
||||
<p><a href="https://github.com/Zagrios/bs-manager">BSManager</a> provides two authentication methods to access your
|
||||
Steam account, ensuring safe and flexible access to
|
||||
different game versions:</p>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<img src="https://github.com/Zagrios/bs-manager/assets/40648115/09d579de-8b8e-45d3-8829-21a25cc7daf8"/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p><a href="https://github.com/Zagrios/bs-manager">BSManager</a> allows you to safely and easily access different versions of Beat Saber, ensuring flexibility for modding or version-specific preferences.</p>
|
||||
<p>To download a version, you can authenticate using two methods:</p>
|
||||
<ul>
|
||||
<li><strong>via Steam</strong>: Use either your credentials, which will not be stored by <a
|
||||
href="https://github.com/Zagrios/bs-manager">BSManager</a> or
|
||||
<a href="https://github.com/SteamRE/DepotDownloader">DepotDownloader</a>, or opt for a quick and secure
|
||||
authentication via QR with the Steam Guard app.</li>
|
||||
<li><strong>via Steam</strong>: Authenticate securely using your Steam credentials or by scanning a QR code with the Steam Guard app.</li>
|
||||
<li><strong>via Oculus</strong>: BSManager redirects you to the official Oculus website for authentication to retrieve your token securely. Alternatively, you can follow <a href="https://github.com/Zagrios/bs-manager/wiki/How-to-obtain-your-Oculus-Token">this guide</a> to obtain your token manually and paste it into BSManager.</li>
|
||||
</ul>
|
||||
<p>For detailed instructions on installing versions, follow this tutorial:
|
||||
👉 <a href="https://github.com/Zagrios/bs-manager/wiki/install-or-import-a-version">How to install or import a version</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<img src="https://github.com/Zagrios/bs-manager/assets/40648115/26da3a19-a936-4e6b-b79b-a139d7bd55de" />
|
||||
|
||||
<img src="https://github.com/Zagrios/bs-manager/assets/40648115/09d579de-8b8e-45d3-8829-21a25cc7daf8" alt=""/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ul>
|
||||
<li><strong>via Oculus</strong>: For Oculus users, <a href="https://github.com/Zagrios/bs-manager">BSManager</a>
|
||||
requires you to retrieve a connection token by following the instructions in this guide: <a href="https://github.com/Zagrios/bs-manager/wiki/How-to-obtain-your-Oculus-Token">How to obtain your Oculus Token</a>. Once obtained, please insert it into the form.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<img height=450 src="https://github.com/Zagrios/bs-manager/assets/40648115/3674e629-542d-4de3-b8db-b248f25126d7" />
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
MULTI-VERSION SHARING
|
||||
----------------------------------------
|
||||
@@ -320,10 +286,9 @@
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<video src="https://github.com/Zagrios/bs-manager/assets/40648115/ba39aceb-f55f-4e58-8386-51fcfaad4957"/>
|
||||
<video src="https://github.com/Zagrios/bs-manager/assets/40648115/ba39aceb-f55f-4e58-8386-51fcfaad4957"/>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
IMPORTING AN EXISTING VERSION OF BEAT SABER
|
||||
----------------------------------------
|
||||
@@ -340,49 +305,53 @@
|
||||
<video src="https://github.com/Zagrios/bs-manager/assets/40648115/cdedb951-d97a-474d-a217-2422be3919f7"/>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
LAUNCH ARGUMENTS
|
||||
----------------------------------------
|
||||
-->
|
||||
<div>
|
||||
<h2>Launch Arguments</h2>
|
||||
<p><a href="https://github.com/Zagrios/bs-manager">BSManager</a> provides efficient management of <a
|
||||
href="https://beatsaber.com/">Beat Saber</a> launch arguments, including three predefined ones to
|
||||
enhance your gaming experience:</p>
|
||||
<p><a href="https://github.com/Zagrios/bs-manager">BSManager</a> provides advanced management of launch arguments for <a href="https://beatsaber.com/">Beat Saber</a>, allowing you to customize your gaming experience.</p>
|
||||
|
||||
<h3>Predefined Arguments</h3>
|
||||
<ul>
|
||||
<li><strong>Oculus Mode</strong>: Launches the game directly in Oculus mode, avoiding the use of STEAM VR.</li>
|
||||
<li><strong>FPFC Mode</strong>: Enables "FPFC" mode, allowing you to control the game without using a VR
|
||||
headset.</li>
|
||||
<li><strong>Debug Mode</strong>: Activates debugging mode, generating Windows log outputs for IPA.</li>
|
||||
<li><strong>Oculus Mode</strong>: Allows you to use Oculus's VR composer while bypassing SteamVR, which may improve performance. (Only useful if you launch the game via Steam).</li>
|
||||
<li><strong>FPFC Mode</strong>: Enables "First Person Flying Controller" mode, allowing you to navigate menus using the keyboard (WASD) and mouse without a VR headset.</li>
|
||||
<li><strong>Debug Mode</strong>: Activates the IPA debug console, displaying logs used by mods.</li>
|
||||
<li><strong>Skip Steam</strong>: Prevents Steam from opening automatically with Beat Saber, useful if you use an alternative VR runtime like WIVRn or Monado.</li>
|
||||
<li><strong>Map Editor</strong>: Launches the official Beat Saber map editor instead of the game.</li>
|
||||
</ul>
|
||||
<p>You also have the freedom to add your own custom launch arguments by inserting them into "Advanced Launch" and
|
||||
separating them with a semicolon (;).</p>
|
||||
|
||||
<h3>Custom Arguments</h3>
|
||||
<p>You can also add your own custom launch arguments by inserting them into "Advanced Launch" and separating them with a semicolon (;).</p>
|
||||
|
||||
<h3>Pinning Arguments</h3>
|
||||
<p>You can now pin your favorite launch arguments for quick access. By default, only <strong>Oculus Mode</strong> and <strong>Debug Mode</strong> are pinned.</p>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<img src="https://github.com/Zagrios/bs-manager/assets/40648115/1244d49f-20e1-400c-9818-528f05cfadf5" />
|
||||
<img src="https://github.com/user-attachments/assets/c247b237-9c49-4e7b-9366-aeb1dd3b7b9a" alt="" />
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
SHORTCUT CREATION
|
||||
----------------------------------------
|
||||
-->
|
||||
<div>
|
||||
<h2>Shortcut Creation</h2>
|
||||
<p>Create shortcuts to easily access your different versions of <a href="https://beatsaber.com/">Beat Saber</a> with
|
||||
<a href="https://github.com/Zagrios/bs-manager">BSManager</a>. After selecting your
|
||||
version, go to the version settings, click on "Create a Shortcut", and then select your launch options. Thus,
|
||||
you can quickly and easily launch your preferred version of <a href="https://beatsaber.com/">Beat Saber</a> with the
|
||||
desired settings.</p>
|
||||
<p>Create shortcuts to easily access your different versions of <a href="https://beatsaber.com/">Beat Saber</a> with <a href="https://github.com/Zagrios/bs-manager">BSManager</a>.
|
||||
</p>
|
||||
<p>To learn how to create a shortcut, follow the tutorial available here:
|
||||
👉 <a href="https://github.com/Zagrios/bs-manager/wiki/create-a-shortcut">Shortcut Creation Guide</a>
|
||||
</p>
|
||||
<p>This allows you to quickly and easily launch your preferred version of <a href="https://beatsaber.com/">Beat Saber</a> with the desired settings.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<video src="https://github.com/Zagrios/bs-manager/assets/40648115/bfa1e97d-6392-4dec-a1fc-bb05048cf6d2" />
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
----------------------------------------
|
||||
DOWNLOAD AND INSTALLATION
|
||||
@@ -392,79 +361,30 @@
|
||||
<hr>
|
||||
<h1><b>Download and Installation</b></h1>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p><a href="https://github.com/Zagrios/bs-manager">BSManager</a> provides centralized and organized management of
|
||||
beatmaps, mods, and models, integrating download features and sharing between <a href="https://beatsaber.com/">Beat
|
||||
Saber</a> versions.</p>
|
||||
<p><a href="https://github.com/Zagrios/bs-manager">BSManager</a> centralizes and simplifies the management of beatmaps, playlists, mods, and models, offering seamless downloads and sharing between different <a href="https://beatsaber.com/">Beat Saber</a> versions.</p>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
MAPS
|
||||
----------------------------------------
|
||||
-->
|
||||
<div>
|
||||
<h2>Maps</h2>
|
||||
<p><a href="https://github.com/Zagrios/bs-manager">BSManager</a> integrates the <a href="https://beatsaver.com">Beat
|
||||
Saver</a> API to provide you with a comprehensive list of maps downloadable directly from its interface. Moreover,
|
||||
the OneClick feature allows you to download maps with one click from various sources such as <a
|
||||
href="https://beatsaver.com">Beat Saver</a>, <a href="https://bsaber.com">BeastSaber</a>, <a
|
||||
href="https://scoresaber.com">ScoreSaber</a>, and <a href="https://www.beatleader.xyz">Beat Leader</a>. To add
|
||||
maps, simply select the desired version or the "shared" tab, then click "ADD".</p>
|
||||
<h2>Features</h2>
|
||||
<p>With <a href="https://github.com/Zagrios/bs-manager">BSManager</a>, you can manage your:
|
||||
</p>
|
||||
<ul>
|
||||
<li><b>Maps:</b> Browse and download maps directly from sources like <a href="https://beatsaver.com">Beat Saver</a>, <a href="https://bsaber.com">BeastSaber</a>, <a href="https://scoresaber.com">ScoreSaber</a>, and <a href="https://www.beatleader.xyz">Beat Leader</a>.</li>
|
||||
<li><b>Playlists:</b> Search for and download playlists directly from BSManager, just like maps. 😉
|
||||
<li><b>Mods:</b> Manage and install mods via the "Mods" tab, offering the same features as Mod Assistant.</li>
|
||||
<li><b>Models:</b> Add and manage models easily with the integration of the <a href="https://modelsaber.com">Model Saber</a> API.</li>
|
||||
</ul>
|
||||
<p>For detailed instructions on each feature, check the following guides:</p>
|
||||
<ul>
|
||||
<li>👉 <a href="https://github.com/Zagrios/bs-manager/wiki/install-any-map">Install Any Map</a></li>
|
||||
<li>👉 <a href="https://github.com/Zagrios/bs-manager/wiki/install-all-playlists-you-want">Install Playlists</a></li>
|
||||
<li>👉 <a href="https://github.com/Zagrios/bs-manager/wiki/install-mods-on-your-version">Install Mods</a></li>
|
||||
<li>👉 <a href="https://github.com/Zagrios/bs-manager/wiki/modify-models-in-your-beat-saber">Manage Models</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<video src="https://github.com/Zagrios/bs-manager/assets/40648115/3f9f9968-ac55-465e-bc28-17aebfc787ea"/>
|
||||
|
||||
</div>
|
||||
|
||||
<!--
|
||||
PLAYLISTS
|
||||
----------------------------------------
|
||||
-->
|
||||
<div>
|
||||
<h2>Playlists</h2>
|
||||
<p>Although the playlist management interface is not yet developed, <a
|
||||
href="https://github.com/Zagrios/bs-manager">BSManager</a> offers a OneClick feature for playlists from <a
|
||||
href="https://beatsaver.com">Beat Saver</a>.</p>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<video src="https://github.com/Zagrios/bs-manager/assets/40648115/733fe6bb-c157-4c47-8a79-f8062380bcc8" />
|
||||
</div>
|
||||
|
||||
<!--
|
||||
MODS
|
||||
----------------------------------------
|
||||
-->
|
||||
<div>
|
||||
<h2>Mods</h2>
|
||||
<p>Manage your mods easily with <a href="https://github.com/Zagrios/bs-manager">BSManager</a>. After downloading a
|
||||
version, navigate to the "Mods" tab of the selected version to manage and install mods. <a
|
||||
href="https://github.com/Zagrios/bs-manager">BSManager</a> operates similarly to Mod Assistant and offers the same
|
||||
mods as this tool.</p>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<video src="https://github.com/Zagrios/bs-manager/assets/40648115/ca8f0727-b2dd-41f3-bbea-9bcaf61a89f1"/>
|
||||
|
||||
</div>
|
||||
|
||||
<!--
|
||||
MODELS
|
||||
----------------------------------------
|
||||
-->
|
||||
<div>
|
||||
<h2>Models</h2>
|
||||
<p>Add and manage models simply via the corresponding tab on the desired version or by going to the "shared" tab. The
|
||||
<a href="https://modelsaber.com">Model Saber</a> API is integrated to provide easy and direct access to models
|
||||
available for <a href="https://beatsaber.com/">Beat Saber</a>.</p>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<video src="https://github.com/Zagrios/bs-manager/assets/40648115/d983b34b-9f4c-4274-aee4-4c1c03dd90d0"/>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
----------------------------------------
|
||||
CUSTOMIZATION
|
||||
@@ -495,7 +415,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
<div align="center">
|
||||
<video src = "https://github.com/Zagrios/bs-manager/assets/40648115/d2c1b0ec-a2f5-41b5-99d5-c4c2cd34cbd4" />
|
||||
</div>
|
||||
|
||||
@@ -520,7 +440,6 @@
|
||||
<video src="https://github.com/Zagrios/bs-manager/assets/40648115/f66a2c99-770e-42ca-9a71-b1806946ad0c" />
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
----------------------------------------
|
||||
MORE INFORMATION
|
||||
@@ -546,7 +465,6 @@
|
||||
GitHub</a>.</p>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
CREDITS
|
||||
----------------------------------------
|
||||
@@ -554,7 +472,7 @@
|
||||
<div>
|
||||
<h2>Credits</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/Zagrios">Zagrios - Mathieu Gries</a> - Lead Developer & Founder.</li>
|
||||
<li><a href="https://github.com/Zagrios">Zagrios</a> - Lead Developer & Founder.</li>
|
||||
<li><a href="https://github.com/Iluhadesu">Iluhadesu</a> - Co-Developer & Co-Founder, Discord Bot Developer.</li>
|
||||
<li><a href="https://github.com/GaetanGrd">GaetanGrd</a> - Co-Developer & Co-Founder, Documentation Lead.</li>
|
||||
<li><a href="https://github.com/cheddZy">cheddZy</a> - Icon Creator.</li>
|
||||
@@ -575,12 +493,11 @@
|
||||
href="https://github.com/Zagrios/bs-manager">BSManager</a>!</p>
|
||||
<div align="center">
|
||||
<a href="https://github.com/Zagrios/bs-manager/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=Zagrios/bs-manager" />
|
||||
<img src="https://contrib.rocks/image?repo=Zagrios/bs-manager" alt="Contributor list" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
----------------------------------------
|
||||
Others
|
||||
|
||||
@@ -813,5 +813,14 @@
|
||||
"ReleaseImg": "https://clan.fastly.steamstatic.com/images/32055887/65df6b24c3b8a7f415080ac01394917cedabf9fc.png",
|
||||
"ReleaseDate": "1733846128",
|
||||
"year": "2024"
|
||||
},
|
||||
{
|
||||
"BSVersion": "1.40.1",
|
||||
"BSManifest": "8655702238937650396",
|
||||
"OculusBinaryId": "8526525354113835",
|
||||
"ReleaseURL": "https://steamcommunity.com/games/620980/announcements/detail/510696972023234581",
|
||||
"ReleaseImg": "https://clan.fastly.steamstatic.com/images/32055887/5cc1ca353151ed31da2764716cdcbe89442aeeeb.png",
|
||||
"ReleaseDate": "1738144954",
|
||||
"year": "2025"
|
||||
}
|
||||
]
|
||||
@@ -818,6 +818,11 @@
|
||||
"multiple": "These maps will also be removed from versions using shared maps"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete-duplicate-maps": {
|
||||
"title": "Delete maps?",
|
||||
"desc": "Only the map \"{map}\" is a duplicate. Are you sure you want to delete it?",
|
||||
"desc-plural": "{nb} duplicate maps have been found. Are you sure you want to delete them?"
|
||||
}
|
||||
},
|
||||
"link-contents":{
|
||||
|
||||
@@ -818,6 +818,11 @@
|
||||
"multiple": "Queste mappe saranno rimosse anche dalle versioni che usano mappe condivise"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete-duplicate-maps": {
|
||||
"title": "Eliminare le mappe?",
|
||||
"desc": "Solo la mappa \"{map}\" è un duplicato. Sei sicuro di volerla eliminare?",
|
||||
"desc-plural": "Sono state trovate {nb} mappe duplicate. Sei sicuro di volerle eliminare?"
|
||||
}
|
||||
},
|
||||
"link-contents":{
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
"description": "Descrição",
|
||||
"dropdown": {
|
||||
"import-mods": "Importar mods",
|
||||
"desinstalar-all": "Desinstalar todos",
|
||||
"uninstall-all": "Desinstalar todos",
|
||||
"unselect-all": "Desmarcar todos"
|
||||
}
|
||||
}
|
||||
@@ -173,7 +173,7 @@
|
||||
"verify-files": "Verificar arquivos",
|
||||
"clone": "Clonar",
|
||||
"edit": "Editar",
|
||||
"desinstalar": "Desintalar",
|
||||
"uninstall": "Desintalar",
|
||||
"create-shortcut": "Criar um atalho",
|
||||
"shared-folders": "Pastas compartilhadas"
|
||||
}
|
||||
@@ -571,7 +571,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"desinstalar-mod": {
|
||||
"uninstall-mod": {
|
||||
"titles": {
|
||||
"success": "Mod desinstalado 🎉"
|
||||
},
|
||||
@@ -581,7 +581,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"desinstalar-all-mods": {
|
||||
"uninstall-all-mods": {
|
||||
"titles": {
|
||||
"success": "Mods desinstalados 🎉"
|
||||
},
|
||||
@@ -753,7 +753,7 @@
|
||||
"submit": "Importar uma versão"
|
||||
}
|
||||
},
|
||||
"bs-desinstalar": {
|
||||
"bs-uninstall": {
|
||||
"title": "Desinstalar",
|
||||
"description": "Você tem certeza que quer desinstalar Beat Saber {version}? Você vai ter que baixar ela novamente se quiser jogá-la.",
|
||||
"buttons": {
|
||||
@@ -789,12 +789,12 @@
|
||||
"submit": "Clonar"
|
||||
}
|
||||
},
|
||||
"desinstalar-mod": {
|
||||
"uninstall-mod": {
|
||||
"title": "Desinstalar",
|
||||
"description": "Você tem certeza que quer desinstalar Beat Saber {mod}? Pode fazer com que outros mods parem de funcionar.",
|
||||
"description-bsipa": "Você tem certeza que quer desinstalar BSIPA? Depois, nenhum mod irá funcionar."
|
||||
},
|
||||
"desinstalar-all-mods": {
|
||||
"uninstall-all-mods": {
|
||||
"title": "Desinstalando mods",
|
||||
"description": "Você tem certeza que quer desinstalar todos os mods da versão {version}? Essa operação não pode ser desfeita."
|
||||
},
|
||||
@@ -818,6 +818,11 @@
|
||||
"multiple": "Esses mapas também serão removidos de suas versões utilizando mapas compartilhados"
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete-duplicate-maps": {
|
||||
"title": "Excluir mapas?",
|
||||
"desc": "Apenas o mapa \"{map}\" é um duplicado. Tem certeza de que deseja excluí-lo?",
|
||||
"desc-plural": "{nb} mapas duplicados foram encontrados. Tem certeza de que deseja excluí-los?"
|
||||
}
|
||||
},
|
||||
"link-contents":{
|
||||
|
||||
@@ -1,12 +1,44 @@
|
||||
## Table of Contents
|
||||
|
||||
- [With BSManager](#with-bsmanager)
|
||||
- [Install playlists](#how-to-install-playlists-with-bsmanager)
|
||||
- [Import playlists](#how-to-import-playlist-with-bsmanager)
|
||||
- [Manual Installation](#manual-installation)
|
||||
- [One-Click Installation](#one-click-installation)
|
||||
|
||||
## With BSManager
|
||||
|
||||
(Coming with version 1.5.0)
|
||||
### How to Install Playlists with BSManager
|
||||
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from one of the following sources:
|
||||
- [GitHub](https://github.com/Zagrios/bs-manager/releases/latest)
|
||||
- [Official Website](https://www.bsmanager.io)
|
||||
- [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files)
|
||||
2. Launch BSManager and select the version of *Beat Saber* where you want to install playlists from the list on the left side of the interface.
|
||||
3. Go to the `Playlist` section by clicking on the __`Maps`__ tab.
|
||||
4. Click the __`Add`__ button, then select __`Browse playlist`__ to open the list of online playlists.
|
||||
5. Use the search bar at the top of the interface to filter playlists or sort the list.
|
||||
6. Download to install it.
|
||||
7. Wait for the installation to complete. Once it's finished, you can launch Beat Saber directly from [BSManager](https://www.bsmanager.io) and enjoy your new playlists!
|
||||
|
||||
> [!NOTE]
|
||||
> You can click on a playlist's image to view the maps it contains and download only the maps you want.
|
||||
|
||||
### How to Import Playlist with BSManager
|
||||
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from one of the following sources:
|
||||
- [GitHub](https://github.com/Zagrios/bs-manager/releases/latest)
|
||||
- [Official Website](https://www.bsmanager.io)
|
||||
- [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files)
|
||||
2. Launch BSManager and select the version of *Beat Saber* where you want to install playlists from the list on the left side of the interface.
|
||||
3. Go to the `playlist` section by clicking on the __`Maps`__ tab.
|
||||
4. From here, there are two possible methods:
|
||||
1. Click the __`Add`__ button, then select __`Import a playlist`__ and choose the file to import.
|
||||
2. Drag and drop your file directly onto the playlist interface in BSManager.
|
||||
5. Wait for the import to complete. Once finished, you can launch Beat Saber directly from [BSManager](https://www.bsmanager.io) and enjoy your new playlists!
|
||||
|
||||
> [!Important]
|
||||
> The file must be in `.bplist` or `.json` format to be imported.
|
||||
|
||||
## Manual Installation
|
||||
|
||||
|
||||
@@ -1,18 +1,41 @@
|
||||
## Table of Contents
|
||||
|
||||
- [With BSManager](#with-bsmanager)
|
||||
- [Install maps](#how-to-install-maps-with-bsmanager)
|
||||
- [Import maps](#how-to-import-maps-with-bsmanager)
|
||||
- [Manual Installation](#manual-installation)
|
||||
- [One-Click Installation](#one-click-installation)
|
||||
|
||||
## With BSManager
|
||||
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from [GitHub](https://github.com/Zagrios/bs-manager/releases/latest).
|
||||
2. Launch [BSManager](https://www.bsmanager.io) and select the Beat Saber version where you want to install maps from the list on the left of the interface.
|
||||
3. Go to maps by clicking on the __`Maps`__ tab.
|
||||
4. Click on the __`Add`__ button to access the list of online maps.
|
||||
5. Find the maps that you want to install from the provided list. You can use the search bar at the top of the interface to filter maps by __`name`__, __`type`__, __`mods`__, __`tags`__, __`notes per second`__, __`duration`__, and order the list by __`latest`__, __`relevance`__, __`rating`__, or __`curated`__.
|
||||
6. Double-click on the map or simply click on the download button to install the map.
|
||||
7. Wait for the installation to complete. Once it's finished, you can launch Beat Saber directly from [BSManager](https://www.bsmanager.io) to enjoy your new maps!
|
||||
### How to Install Maps with BSManager
|
||||
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from one of the following sources:
|
||||
- [GitHub](https://github.com/Zagrios/bs-manager/releases/latest)
|
||||
- [Official Website](https://www.bsmanager.io)
|
||||
- [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files)
|
||||
2. Launch BSManager and select the version of *Beat Saber* where you want to install maps from the list on the left side of the interface.
|
||||
3. Go to the `Maps` section by clicking on the __`Maps`__ tab.
|
||||
4. Click the __`Add`__ button to access the list of online maps.
|
||||
5. Use the search bar at the top of the interface to filter maps by __`name`__, __`type`__, __`mods`__, __`tags`__, __`notes per second`__, or __`duration`__, and order the list by __`latest`__, __`relevance`__, __`rating`__, or __`curated`__.
|
||||
6. Download to install it.
|
||||
7. Wait for the installation to complete. Once it's finished, you can launch Beat Saber directly from [BSManager](https://www.bsmanager.io) to enjoy your new maps!
|
||||
|
||||
### How to Import Maps with BSManager
|
||||
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from one of the following sources:
|
||||
- [GitHub](https://github.com/Zagrios/bs-manager/releases/latest)
|
||||
- [Official Website](https://www.bsmanager.io)
|
||||
- [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files)
|
||||
2. Launch BSManager and select the version of *Beat Saber* where you want to install maps from the list on the left side of the interface.
|
||||
3. Go to the `Maps` section by clicking on the __`Maps`__ tab.
|
||||
4. From here, there are two possible methods:
|
||||
1. Click the __`Add`__ button, then select __`Import maps`__ and choose the .zip file to import.
|
||||
2. Drag and drop your .zip file directly onto the maps interface in BSManager.
|
||||
5. Wait for the import to complete. Once finished, you can launch Beat Saber directly from [BSManager](https://www.bsmanager.io) to enjoy your new maps!
|
||||
|
||||
> [!Important]
|
||||
> The file must be in `.zip` format to be imported.
|
||||
|
||||
## Manual Installation
|
||||
|
||||
|
||||
@@ -12,17 +12,20 @@
|
||||
- [With BSManager](#with-bsmanager)
|
||||
- [One-Click Installation](#one-click-installation)
|
||||
|
||||
# With BSManager
|
||||
## With BSManager
|
||||
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from [GitHub](https://github.com/Zagrios/bs-manager/releases/latest), [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files), or [Website](https://www.bsmanager.io).
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from one of the following sources:
|
||||
- [GitHub](https://github.com/Zagrios/bs-manager/releases/latest)
|
||||
- [Official Website](https://www.bsmanager.io)
|
||||
- [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files)
|
||||
2. Launch [BSManager](https://www.bsmanager.io) and select the Beat Saber version where you want to install models from the list on the left of the interface.
|
||||
3. Go to models by clicking on the __`Models`__ tab.
|
||||
4. Click on the __`Add`__ button to access the list of online models.
|
||||
5. Find the models that you want to install from the provided list. You can use the search bar at the top of the interface to filter models by __`name`__, __`type`__, and order the list by __`date`__, __`name`__, or __`author`__.
|
||||
6. Double-click on the model or simply click on the download button to install the model.
|
||||
6. Download to install the model.
|
||||
7. Wait for the installation to complete. Once it's finished, you can launch Beat Saber directly from [BSManager](https://www.bsmanager.io) to enjoy your new models!
|
||||
|
||||
# One-Click Installation
|
||||
## One-Click Installation
|
||||
|
||||
1. Launch [BSManager](https://www.bsmanager.io) and make sure that the One-Click installation feature is enabled for models.
|
||||
2. Go to one of the supported websites and search for the models you want to install.
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
## Table of Contents
|
||||
|
||||
- [With BSManager](#with-bsmanager)
|
||||
- [Install mods](#how-to-install-mods-with-bsmanager)
|
||||
- [Import mods](#how-to-import-mods-with-bsmanager)
|
||||
- [Manual Installation](#manual-installation)
|
||||
|
||||
## With BSManager
|
||||
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from [GitHub](https://github.com/Zagrios/bs-manager/releases/latest), [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files), or the [official website](https://www.bsmanager.io).
|
||||
### How to Install Mods with BSManager
|
||||
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from one of the following sources:
|
||||
- [GitHub](https://github.com/Zagrios/bs-manager/releases/latest)
|
||||
- [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files)
|
||||
- [Official Website](https://www.bsmanager.io)
|
||||
2. Launch [BSManager](https://www.bsmanager.io) and select the version of Beat Saber that you want to use from the list on the left side of the interface.
|
||||
3. Go to mods by clicking on the __`Mods`__ tab.
|
||||
4. Find the mods that you want to install from the provided list. You can use the search bar at the top of the interface to filter mods by name.
|
||||
@@ -13,6 +20,22 @@
|
||||
6. Click __`Install or Update`__ at the bottom-right corner of the interface.
|
||||
7. Wait for the installation to complete. Once it's finished, you can launch Beat Saber directly from [BSManager](https://www.bsmanager.io) to enjoy your new mods!
|
||||
|
||||
### How to Import Mods with BSManager
|
||||
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from one of the following sources:
|
||||
- [GitHub](https://github.com/Zagrios/bs-manager/releases/latest)
|
||||
- [Official Website](https://www.bsmanager.io)
|
||||
- [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files)
|
||||
2. Launch BSManager and select the version of *Beat Saber* where you want to install mods from the list on the left side of the interface.
|
||||
3. Go to the `Mods` tab.
|
||||
4. From here, there are two possible methods:
|
||||
1. Click on __`⁝`__ button, then select __`Import mods`__ and choose the .zip or .dll file to import.
|
||||
2. Drag and drop your .zip or .dll file directly onto the mods interface in BSManager.
|
||||
5. Wait for the import to complete. Once finished, you can launch Beat Saber directly from [BSManager](https://www.bsmanager.io) to enjoy your new mods!
|
||||
|
||||
> [!Important]
|
||||
> The file must be in `.zip` or `.dll` format to be imported.
|
||||
|
||||
## Manual Installation
|
||||
|
||||
> [!IMPORTANT]
|
||||
|
||||
|
After Width: | Height: | Size: 112 KiB |
@@ -15,6 +15,10 @@
|
||||
4. Choose your launch options.
|
||||
5. Click on __`Create a Shortcut`__. It should now appear on your desktop.
|
||||
|
||||
<div align="center">
|
||||
<img src="../wiki/Guides/Installation-and-updates/Create-a-shortcut/BsmShortcutInterface.png" alt="SeeTxtFile.png" />
|
||||
</div>
|
||||
|
||||
### On Taskbar
|
||||
|
||||
1. Open Notepad and drag & drop the version shortcut you need.
|
||||
@@ -31,25 +35,12 @@ start <version link>
|
||||
|
||||
## How to create a shortcut on Steam
|
||||
|
||||
1. In [BSManager](https://www.bsmanager.io), select the version you want to open in Steam from the list on the left side.
|
||||
2. Click the gear icon at the top-right corner and select __`Create a Shortcut`__. This will create a shortcut on your desktop for launching the game.
|
||||
3. Open Notepad (or any other text editor) and drag & drop the shortcut file into the text editor. You will see a string of text similar to this:
|
||||
|
||||
```yaml
|
||||
bsmanager://launch/?version=1.29.1&versionIno=3940649674381032
|
||||
```
|
||||
|
||||
4. Copy this text and remove any spaces.
|
||||
|
||||
<div align="center">
|
||||
<img src="../wiki/Guides/Installation-and-updates/Create-a-shortcut/SeeTxtFile.png" alt="SeeTxtFile.png" />
|
||||
</div>
|
||||
|
||||
5. Open Steam and add [BSManager](https://www.bsmanager.io) as a non-Steam game.
|
||||
6. In your Steam library, right-click on [BSManager](https://www.bsmanager.io), select __`Properties`__, and add the link from step 4 as a launch parameter.
|
||||
7. Disable "Desktop Game Theatre" and scroll down to enable "Include in VR Library" so you can launch it from your VR headset.
|
||||
8. Rename this entry to whatever you want and customize the name and the icon of the shortcut if desired.
|
||||
1. Launch [BSManager](https://www.bsmanager.io) and select your version.
|
||||
2. Click on the gear icon at the top-right corner of the version.
|
||||
3. Click on the __`Create a Shortcut`__ button.
|
||||
4. Choose your launch options.
|
||||
5. Click on __`Create a Shortcut`__. It should now appear on your desktop.
|
||||
|
||||
<div align="center">
|
||||
<img src="../wiki/Guides/Installation-and-updates/Create-a-shortcut/SteamShortcut.png" alt="SteamShortcut.png" />
|
||||
<img src="../wiki/Guides/Installation-and-updates/Create-a-shortcut/SteamShortcutCheck.png" alt="SeeTxtFile.png" />
|
||||
</div>
|
||||
|
||||
|
Before Width: | Height: | Size: 256 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -10,7 +10,10 @@
|
||||
|
||||
## Install a Version with Steam
|
||||
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from [GitHub](https://github.com/Zagrios/bs-manager/releases/latest), [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files), or the [official website](https://www.bsmanager.io).
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from one of the following sources:
|
||||
- [GitHub](https://github.com/Zagrios/bs-manager/releases/latest)
|
||||
- [Official Website](https://www.bsmanager.io)
|
||||
- [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files)
|
||||
2. Open the __`Add Versions`__ interface and select the version you wish to download.
|
||||
3. Choose __`Steam`__ as your platform.
|
||||
4. Enter your Steam credentials (ID and password) or scan the QR code using the Steam app on your phone.
|
||||
@@ -28,15 +31,29 @@
|
||||
> - Ensure that Beat Saber is in your __Oculus PCVR__ library to enable downgrading.
|
||||
> - You must run Beat Saber at least once on your computer before attempting to launch retrograde Oculus versions from [BSManager](https://www.bsmanager.io).
|
||||
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from [GitHub](https://github.com/Zagrios/bs-manager/releases/latest), [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files), or the [official website](https://www.bsmanager.io).
|
||||
1. Download and install [BSManager](https://www.bsmanager.io) from one of the following sources:
|
||||
- [GitHub](https://github.com/Zagrios/bs-manager/releases/latest)
|
||||
- [Official Website](https://www.bsmanager.io)
|
||||
- [Nexus Mods](https://www.nexusmods.com/beatsaber/mods/18?tab=files)
|
||||
2. Open the __`Add Versions`__ interface and select the version you wish to download.
|
||||
3. Choose __`Oculus Store`__ as your platform.
|
||||
4. Follow [this guide](How-to-obtain-your-Oculus-Token) to obtain your Oculus token.
|
||||
5. Once logged into Oculus, the download will start automatically. Please wait for it to complete.
|
||||
6. When the download is finished, select the downloaded version and click the __`Launch`__ button to verify that the game launches correctly.
|
||||
4. Click on __`Connect to Meta`__ or __`Enter my login token manually`__ based on your preferred login method.
|
||||
|
||||
### If you selected __`Connect to Meta`__
|
||||
|
||||
1. You will be redirected to the Oculus website. Enter your email and follow the instructions to log in.
|
||||
2. Once logged in, the download will start automatically. Please wait for it to complete.
|
||||
3. When the download finishes, select the downloaded version and click the __`Launch`__ button to verify that the game launches correctly.
|
||||
|
||||
### If you selected __`Enter my login token manually`__
|
||||
|
||||
1. Follow [this guide](How-to-obtain-your-Oculus-Token) to obtain your Oculus token.
|
||||
2. Once the token is entered and validated, the download will start automatically. Please wait for it to complete.
|
||||
3. When the download finishes, select the downloaded version and click the __`Launch`__ button to verify that the game launches correctly.
|
||||
|
||||
<div align="center">
|
||||
<img src="../wiki/Guides/Installation-and-updates/Install-or-import-a-version/OculusLogin.png" alt="OculusLogin.png" />
|
||||
<img src="../wiki/Guides/Installation-and-updates/Install-or-import-a-version/OculusLoginToken.png" alt="OculusLoginToken.png" />
|
||||
</div>
|
||||
|
||||
## How to Import a Version
|
||||
|
||||
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 125 KiB |
|
After Width: | Height: | Size: 66 KiB |
@@ -2,7 +2,7 @@
|
||||
"name": "bs-manager",
|
||||
"description": "Manage maps, mods and more for Beat Saber",
|
||||
"main": "./.erb/dll/main.bundle.dev.js",
|
||||
"version": "1.5.0-alpha.10",
|
||||
"version": "1.5.2",
|
||||
"scripts": {
|
||||
"build-rust-scripts": "ts-node ./.erb/scripts/build-rust-scripts.js",
|
||||
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "bs-manager",
|
||||
"version": "1.5.0-alpha.10",
|
||||
"version": "1.5.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bs-manager",
|
||||
"version": "1.5.0-alpha.10",
|
||||
"version": "1.5.2",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "bs-manager",
|
||||
"version": "1.5.0-alpha.10",
|
||||
"version": "1.5.2",
|
||||
"description": "BSManager",
|
||||
"main": "./dist/main/main.js",
|
||||
"author": {
|
||||
|
||||
@@ -2,6 +2,15 @@ import { mkdir, pathExistsSync, rm, writeFile } from "fs-extra";
|
||||
import { getSize } from "main/helpers/fs.helpers";
|
||||
import path from "path";
|
||||
|
||||
jest.mock("electron", () => ({ app: {
|
||||
getPath: () => "",
|
||||
getName: () => "",
|
||||
}}));
|
||||
jest.mock("electron-log", () => ({
|
||||
info: jest.fn(),
|
||||
error: jest.fn(),
|
||||
}));
|
||||
|
||||
const TEST_FOLDER = path.resolve(__dirname, "..", "assets", "fs");
|
||||
|
||||
describe("Test fs.helpers getSize", () => {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CopyOptions, MoveOptions, copy, createReadStream, ensureDir, move, pathExists, pathExistsSync, realpath, stat, symlink } from "fs-extra";
|
||||
import { access, mkdir, rm, readdir, unlink, lstat, readlink } from "fs/promises";
|
||||
import { CopyOptions, MoveOptions, RmOptions, copy, createReadStream, ensureDir, move, pathExists, pathExistsSync, realpath, rm, stat, symlink, unlink, unlinkSync } from "fs-extra";
|
||||
import { access, mkdir, readdir, lstat, readlink } from "fs/promises";
|
||||
import path from "path";
|
||||
import { Observable, concatMap, from } from "rxjs";
|
||||
import log from "electron-log";
|
||||
@@ -28,18 +28,53 @@ export async function ensureFolderExist(path: string): Promise<void> {
|
||||
.then(() => {});
|
||||
}
|
||||
|
||||
export async function deleteFolder(folderPath: string): Promise<void> {
|
||||
export async function deleteFile(filepath: string) {
|
||||
try {
|
||||
log.info("Deleting file", `"${filepath}"`);
|
||||
await unlink(filepath);
|
||||
} catch (error: any) {
|
||||
log.error("Could not delete file", `"${filepath}"`);
|
||||
throw CustomError.fromError(error, "generic.fs.delete-file");
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteFileSync(filepath: string) {
|
||||
try {
|
||||
log.info("Deleting file", `"${filepath}"`);
|
||||
unlinkSync(filepath);
|
||||
} catch (error: any) {
|
||||
log.error("Could not delete file", `"${filepath}"`);
|
||||
throw CustomError.fromError(error, "generic.fs.delete-file");
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteFolder(folderPath: string, options?: RmOptions) {
|
||||
if (!(await pathExist(folderPath))) {
|
||||
return;
|
||||
}
|
||||
return rm(folderPath, { recursive: true, force: true });
|
||||
|
||||
try {
|
||||
options = options || { recursive: true, force: true };
|
||||
log.info("Deleting folder", `"${folderPath}"`, options);
|
||||
await rm(folderPath, options);
|
||||
} catch (error: any) {
|
||||
log.error("Could not delete folder", `"${folderPath}"`);
|
||||
throw CustomError.fromError(error, "generic.fs.delete-folder");
|
||||
}
|
||||
}
|
||||
|
||||
export async function unlinkPath(path: string): Promise<void> {
|
||||
if (!(await pathExist(path))) {
|
||||
export function deleteFolderSync(folderPath: string, options?: RmOptions) {
|
||||
if (!pathExistsSync(folderPath)) {
|
||||
return;
|
||||
}
|
||||
return unlink(path);
|
||||
|
||||
try {
|
||||
options = options || { recursive: true, force: true };
|
||||
log.info("Deleting folder", `"${folderPath}"`, options);
|
||||
} catch (error: any) {
|
||||
log.error("Could not delete folder", `"${folderPath}"`);
|
||||
throw CustomError.fromError(error, "generic.fs.delete-folder");
|
||||
}
|
||||
}
|
||||
|
||||
export async function getFoldersInFolder(folderPath: string, opts?: { ignoreSymlinkTargetError?: boolean }): Promise<string[]> {
|
||||
@@ -97,23 +132,22 @@ export function moveFolderContent(src: string, dest: string, option?: MoveOption
|
||||
const files = await readdir(src, { encoding: "utf-8", withFileTypes: true });
|
||||
progress.total = files.length;
|
||||
|
||||
for(const file of files){
|
||||
for (const file of files) {
|
||||
const srcFullPath = path.join(src, file.name);
|
||||
const destFullPath = path.join(dest, file.name);
|
||||
|
||||
const srcChilds = file.isDirectory() ? await readdir(srcFullPath, { encoding: "utf-8", recursive: true }) : [];
|
||||
const allChildsAlreadyExist = srcChilds.every(child => pathExistsSync(path.join(destFullPath, child)));
|
||||
|
||||
if(file.isFile() || !allChildsAlreadyExist){
|
||||
if (file.isFile() || !allChildsAlreadyExist) {
|
||||
const prevSize = await getSize(srcFullPath);
|
||||
await move(srcFullPath, destFullPath, option);
|
||||
const afterSize = await getSize(destFullPath);
|
||||
|
||||
// The size after moving should be the same or greater than the size before moving but never less
|
||||
if(afterSize < prevSize){
|
||||
if (afterSize < prevSize) {
|
||||
throw new CustomError(`File size mismath. before: ${prevSize}, after: ${afterSize} (${srcFullPath})`, "FILE_SIZE_MISMATCH");
|
||||
}
|
||||
|
||||
} else {
|
||||
log.info(`Skipping ${srcFullPath} to ${destFullPath}, all child already exist in destination`);
|
||||
}
|
||||
@@ -121,7 +155,9 @@ export function moveFolderContent(src: string, dest: string, option?: MoveOption
|
||||
progress.current++;
|
||||
subscriber.next(progress);
|
||||
}
|
||||
})().catch(err => subscriber.error(CustomError.fromError(err, err?.code))).finally(() => subscriber.complete());
|
||||
})()
|
||||
.catch(err => subscriber.error(CustomError.fromError(err, err?.code)))
|
||||
.finally(() => subscriber.complete());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -160,7 +196,7 @@ export async function copyDirectoryWithJunctions(src: string, dest: string, opti
|
||||
await copy(sourcePath, destinationPath, options);
|
||||
} else if (item.isSymbolicLink()) {
|
||||
if (options?.overwrite) {
|
||||
await unlinkPath(destinationPath);
|
||||
await deleteFile(destinationPath);
|
||||
}
|
||||
const symlinkTarget = await readlink(sourcePath);
|
||||
const relativePath = path.relative(src, symlinkTarget);
|
||||
@@ -180,8 +216,7 @@ export function hashFile(filePath: string, algorithm = "sha256"): Promise<string
|
||||
});
|
||||
}
|
||||
|
||||
export async function dirSize(dirPath: string): Promise<number>{
|
||||
|
||||
export async function dirSize(dirPath: string): Promise<number> {
|
||||
const entries = await readdir(dirPath);
|
||||
|
||||
const paths = entries.map(async entry => {
|
||||
@@ -200,11 +235,10 @@ export async function dirSize(dirPath: string): Promise<number>{
|
||||
return 0;
|
||||
});
|
||||
|
||||
return (await Promise.all(paths)).flat(Infinity).reduce((acc, size ) => acc + size, 0);
|
||||
return (await Promise.all(paths)).flat(Infinity).reduce((acc, size) => acc + size, 0);
|
||||
}
|
||||
|
||||
export function rxCopy(src: string, dest: string, option?: CopyOptions): Observable<Progression> {
|
||||
|
||||
const dirSizePromise = dirSize(src).catch(err => {
|
||||
log.error("dirSizePromise", err);
|
||||
return 0;
|
||||
@@ -216,15 +250,19 @@ export function rxCopy(src: string, dest: string, option?: CopyOptions): Observa
|
||||
|
||||
return new Observable<Progression>(sub => {
|
||||
sub.next(progress);
|
||||
copy(src, dest, {...option, filter: (src) => {
|
||||
stat(src).then(stats => {
|
||||
progress.current += stats.size;
|
||||
sub.next(progress);
|
||||
});
|
||||
return true;
|
||||
}})
|
||||
.then(() => sub.complete()).catch(err => sub.error(err))
|
||||
})
|
||||
copy(src, dest, {
|
||||
...option,
|
||||
filter: src => {
|
||||
stat(src).then(stats => {
|
||||
progress.current += stats.size;
|
||||
sub.next(progress);
|
||||
});
|
||||
return true;
|
||||
},
|
||||
})
|
||||
.then(() => sub.complete())
|
||||
.catch(err => sub.error(err));
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -257,7 +295,7 @@ export function ensurePathNotAlreadyExistSync(path: string): string {
|
||||
return destPath;
|
||||
}
|
||||
|
||||
export async function isJunction(path: string): Promise<boolean>{
|
||||
export async function isJunction(path: string): Promise<boolean> {
|
||||
const [stats, lstats] = await Promise.all([stat(path), lstat(path)]);
|
||||
return lstats.isSymbolicLink() && stats.isDirectory();
|
||||
}
|
||||
@@ -265,7 +303,7 @@ export async function isJunction(path: string): Promise<boolean>{
|
||||
export function resolveGUIDPath(guidPath: string): string {
|
||||
const guidVolume = path.parse(guidPath).root;
|
||||
const command = `powershell -command "(Get-WmiObject -Class Win32_Volume | Where-Object { $_.DeviceID -like '${guidVolume}' }).DriveLetter"`;
|
||||
const {result: driveLetter, error} = tryit(() => execSync(command).toString().trim());
|
||||
const { result: driveLetter, error } = tryit(() => execSync(command).toString().trim());
|
||||
if (!driveLetter || error) {
|
||||
throw new Error("Unable to resolve GUID path", error);
|
||||
}
|
||||
@@ -292,7 +330,7 @@ export async function getSize(targetPath: string, maxDepth = 5): Promise<number>
|
||||
const visited = new Set<string>();
|
||||
|
||||
const computeSize = async (currentPath: string, depth: number): Promise<number> => {
|
||||
if (visited.has(currentPath)){
|
||||
if (visited.has(currentPath)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -309,9 +347,7 @@ export async function getSize(targetPath: string, maxDepth = 5): Promise<number>
|
||||
}
|
||||
|
||||
const entries = await readdir(currentPath);
|
||||
const sizes = await Promise.all(
|
||||
entries.map((entry) => computeSize(path.join(currentPath, entry), depth + 1))
|
||||
);
|
||||
const sizes = await Promise.all(entries.map(entry => computeSize(path.join(currentPath, entry), depth + 1)));
|
||||
return sizes.reduce((acc, cur) => acc + cur, 0);
|
||||
};
|
||||
|
||||
|
||||
@@ -23,9 +23,11 @@ import { LivShortcut } from "./services/liv/liv-shortcut.service";
|
||||
import { SteamLauncherService } from "./services/bs-launcher/steam-launcher.service";
|
||||
import { FileAssociationService } from "./services/file-association.service";
|
||||
import { SongDetailsCacheService } from "./services/additional-content/maps/song-details-cache.service";
|
||||
import { Dirent, readdirSync, rmSync, unlinkSync } from "fs-extra";
|
||||
import { Dirent, readdirSync } from "fs-extra";
|
||||
import { StaticConfigurationService } from "./services/static-configuration.service";
|
||||
import { configureProxy } from './helpers/proxy.helpers';
|
||||
import { deleteFileSync, deleteFolderSync } from "./helpers/fs.helpers";
|
||||
import { tryit } from "shared/helpers/error.helpers";
|
||||
|
||||
const isDebug = process.env.NODE_ENV === "development" || process.env.DEBUG_PROD === "true";
|
||||
const staticConfig = StaticConfigurationService.getInstance();
|
||||
@@ -238,12 +240,7 @@ function deleteOldLogs(): void {
|
||||
|
||||
for (const folder of deleteLogFolders) {
|
||||
const folderPath = path.join(folder.parentPath, folder.name);
|
||||
try {
|
||||
rmSync(folderPath, { recursive: true, force: true });
|
||||
log.info("Deleted log folder:", folderPath);
|
||||
} catch (error) {
|
||||
log.error("Error deleting folder:", folderPath, error);
|
||||
}
|
||||
tryit(() => deleteFolderSync(folderPath));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,12 +252,7 @@ function deleteOldestLogs(): void {
|
||||
|
||||
for (const file of logs) {
|
||||
const filepath = path.join(file.parentPath, file.name);
|
||||
try {
|
||||
unlinkSync(filepath);
|
||||
log.info("Deleted log file:", filepath);
|
||||
} catch (error) {
|
||||
log.error("Error deleting file:", filepath, error);
|
||||
}
|
||||
tryit(() => deleteFileSync(filepath));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import path from "path";
|
||||
import { RequestService } from "../request.service";
|
||||
import { copyFileSync } from "fs-extra";
|
||||
import sanitize from "sanitize-filename";
|
||||
import { Progression, ensureFolderExist, unlinkPath } from "../../helpers/fs.helpers";
|
||||
import { Progression, deleteFile, ensureFolderExist } from "../../helpers/fs.helpers";
|
||||
import { MODEL_FILE_EXTENSIONS, MODEL_TYPES, MODEL_TYPE_FOLDERS } from "../../../shared/models/models/constants";
|
||||
import { InstallationLocationService } from "../installation-location.service";
|
||||
import { Observable, Subscription, lastValueFrom } from "rxjs";
|
||||
@@ -105,7 +105,9 @@ export class LocalModelsManagerService {
|
||||
}
|
||||
|
||||
public async oneClickDownloadModel(model: MSModel): Promise<void> {
|
||||
if (!model) { return; }
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
|
||||
const versions = await this.localVersion.getInstalledVersions();
|
||||
const downloaded = await lastValueFrom(this.downloadModel(model, versions.pop()));
|
||||
@@ -198,7 +200,7 @@ export class LocalModelsManagerService {
|
||||
};
|
||||
|
||||
for (const model of models) {
|
||||
await unlinkPath(model.path);
|
||||
await deleteFile(model.path);
|
||||
progression.data.push(model);
|
||||
progression.current = progression.data.length;
|
||||
subscriber.next(progression);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { BPList, DownloadPlaylistProgressionData, PlaylistSong } from "shared/mo
|
||||
import { readFileSync, Stats } from "fs";
|
||||
import { BeatSaverService } from "../thrid-party/beat-saver/beat-saver.service";
|
||||
import { copy, ensureDir, pathExists, pathExistsSync, realpath, writeFileSync } from "fs-extra";
|
||||
import { Progression, getUniqueFileNamePath, unlinkPath } from "../../helpers/fs.helpers";
|
||||
import { Progression, deleteFile, getUniqueFileNamePath } from "../../helpers/fs.helpers";
|
||||
import { FileAssociationService } from "../file-association.service";
|
||||
import { SongDetailsCacheService } from "./maps/song-details-cache.service";
|
||||
import { sToMs } from "shared/helpers/time.helpers";
|
||||
@@ -409,7 +409,7 @@ export class LocalPlaylistsManagerService {
|
||||
}
|
||||
|
||||
public deletePlaylistFile(bpList: LocalBPList): Observable<void>{
|
||||
return from(unlinkPath(bpList.path));
|
||||
return from(deleteFile(bpList.path));
|
||||
}
|
||||
|
||||
public exportPlaylists(opt: {version?: BSVersion, bpLists: LocalBPList[], dest: string, playlistsMaps?: BsmLocalMap[]}): Observable<Progression<string>> {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { InstallationLocationService } from "../../installation-location.service
|
||||
import { UtilsService } from "../../utils.service";
|
||||
import crypto, { BinaryLike } from "crypto";
|
||||
import { lstatSync } from "fs";
|
||||
import { copy, createReadStream, ensureDir, pathExists, pathExistsSync, realpath, unlink } from "fs-extra";
|
||||
import { copy, createReadStream, ensureDir, pathExists, pathExistsSync, realpath } from "fs-extra";
|
||||
import { RequestService } from "../../request.service";
|
||||
import sanitize from "sanitize-filename";
|
||||
import { DeepLinkService } from "../../deep-link.service";
|
||||
@@ -15,7 +15,7 @@ import log from "electron-log";
|
||||
import { WindowManagerService } from "../../window-manager.service";
|
||||
import { Observable, Subject, lastValueFrom } from "rxjs";
|
||||
import { Archive } from "../../../models/archive.class";
|
||||
import { Progression, deleteFolder, ensureFolderExist, getFilesInFolder, getFoldersInFolder, pathExist } from "../../../helpers/fs.helpers";
|
||||
import { Progression, deleteFile, deleteFolder, ensureFolderExist, getFilesInFolder, getFoldersInFolder, pathExist } from "../../../helpers/fs.helpers";
|
||||
import { readFile } from "fs/promises";
|
||||
import { FolderLinkerService } from "../../folder-linker.service";
|
||||
import { allSettled } from "../../../../shared/helpers/promise.helpers";
|
||||
@@ -280,6 +280,8 @@ export class LocalMapsManagerService {
|
||||
return new Observable<Progression>(observer => {
|
||||
const progress: Progression = { total: hashs.length, current: 0 };
|
||||
|
||||
observer.next(progress);
|
||||
|
||||
(async () => {
|
||||
const versionMapsPath = await this.getMapsFolderPath(version);
|
||||
const mapsPaths = await getFoldersInFolder(versionMapsPath);
|
||||
@@ -447,7 +449,7 @@ export class LocalMapsManagerService {
|
||||
const zip = await BsmZipExtractor.fromPath(zipPath);
|
||||
await zip.extract(mapPath);
|
||||
zip.close();
|
||||
await unlink(zipPath);
|
||||
await deleteFile(zipPath);
|
||||
|
||||
const localMap = await this.loadMapInfoFromPath(mapPath);
|
||||
localMap.songDetails = this.songDetailsCache.getSongDetails(localMap.hash);
|
||||
|
||||
@@ -139,9 +139,11 @@ export abstract class AbstractLauncherService {
|
||||
for (const [ key, value ] of Object.entries(parseEnvString(envString))) {
|
||||
if (key in env) {
|
||||
log.warn("Ignoring", `${key}=${value}`, "already set env launch command");
|
||||
} else {
|
||||
log.info("Injecting", `${key}="${value}"`, "to the env launch command");
|
||||
continue;
|
||||
}
|
||||
|
||||
log.info("Injecting", `${key}="${value}"`, "to the env launch command");
|
||||
env[key] = value;
|
||||
}
|
||||
|
||||
launchOptions.command = command.substring(index + this.COMMAND_FORMAT.length);
|
||||
|
||||
@@ -89,7 +89,7 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto
|
||||
obs.next({type: BSLaunchWarning.UNABLE_TO_LAUNCH_STEAM});
|
||||
});
|
||||
}
|
||||
else {
|
||||
else if(skipSteam) {
|
||||
obs.next({ type: BSLaunchEvent.SKIPPING_STEAM_LAUNCH});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { InstallationLocationService } from "./installation-location.service";
|
||||
import log from "electron-log";
|
||||
import { deleteFolder, ensureFolderExist, moveFolderContent, pathExist, unlinkPath } from "../helpers/fs.helpers";
|
||||
import { deleteFile, deleteFileSync, deleteFolder, deleteFolderSync, ensureFolderExist, moveFolderContent, pathExist } from "../helpers/fs.helpers";
|
||||
import { lstat, symlink } from "fs/promises";
|
||||
import path from "path";
|
||||
import { copy, mkdirSync, readlink, rmSync, symlinkSync, unlinkSync } from "fs-extra";
|
||||
import { copy, mkdirSync, readlink, symlinkSync } from "fs-extra";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
import { noop } from "shared/helpers/function.helpers";
|
||||
import { StaticConfigurationService } from "./static-configuration.service";
|
||||
@@ -56,8 +56,8 @@ export class FolderLinkerService {
|
||||
});
|
||||
|
||||
tryit(() => {
|
||||
rmSync(testFolder, { force: true, recursive: true });
|
||||
unlinkSync(testLink);
|
||||
deleteFolderSync(testFolder);
|
||||
deleteFileSync(testLink);
|
||||
});
|
||||
|
||||
if(resLink.error){
|
||||
@@ -113,7 +113,7 @@ export class FolderLinkerService {
|
||||
if (isTargetedToSharedPath) {
|
||||
return;
|
||||
}
|
||||
await unlinkPath(folderPath);
|
||||
await deleteFile(folderPath);
|
||||
|
||||
log.info(`Linking ${folderPath} to ${sharedPath}; type: ${this.linkingType}`);
|
||||
return symlink(sharedPath, folderPath, this.getLinkingType());
|
||||
@@ -141,7 +141,7 @@ export class FolderLinkerService {
|
||||
if (!(await this.isFolderSymlink(folderPath))) {
|
||||
return;
|
||||
}
|
||||
await unlinkPath(folderPath);
|
||||
await deleteFile(folderPath);
|
||||
|
||||
const sharedPath = await this.getSharedFolder(folderPath, options?.intermediateFolder);
|
||||
|
||||
|
||||
@@ -67,8 +67,8 @@ export class BeatModsApiService {
|
||||
return this.requestService.getJSON<{ modVersions: BbmModVersion[] }>(`${this.MODS_REPO_API_URL}/hashlookup?hash=${hash}`).then(({ data }) => {
|
||||
this.updateModsHashCache(data?.modVersions ?? []);
|
||||
return data?.modVersions?.at(0);
|
||||
}).catch((e): undefined => {
|
||||
log.error(`Failed to get mod by hash: ${hash}`, e);
|
||||
}).catch((): undefined => {
|
||||
log.error(`Failed to get mod by hash: ${hash}`);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import md5File from "md5-file";
|
||||
import { RequestService } from "../request.service";
|
||||
import { BS_EXECUTABLE } from "../../constants";
|
||||
import log from "electron-log";
|
||||
import { deleteFolder, pathExist, Progression, unlinkPath } from "../../helpers/fs.helpers";
|
||||
import { deleteFile, deleteFolder, pathExist, Progression } from "../../helpers/fs.helpers";
|
||||
import { lastValueFrom, Observable } from "rxjs";
|
||||
import recursiveReadDir from "recursive-readdir";
|
||||
import { sToMs } from "../../../shared/helpers/time.helpers";
|
||||
import { copyFile, ensureDir, pathExistsSync, readdirSync, rm, unlink } from "fs-extra";
|
||||
import { copyFile, ensureDir, pathExistsSync, readdirSync } from "fs-extra";
|
||||
import { CustomError } from "shared/models/exceptions/custom-error.class";
|
||||
import { popElement } from "shared/helpers/array.helpers";
|
||||
import { LinuxService } from "../linux.service";
|
||||
@@ -96,8 +96,14 @@ export class BsModsManagerService {
|
||||
return mod;
|
||||
});
|
||||
|
||||
const mods = await Promise.all(promises);
|
||||
return mods.filter(Boolean);
|
||||
const results = await Promise.allSettled(promises);
|
||||
|
||||
return results.reduce((mods, mod) => {
|
||||
if (mod?.status === "fulfilled" && mod?.value) {
|
||||
mods.push(mod.value);
|
||||
}
|
||||
return mods;
|
||||
}, [] as BbmModVersion[]);
|
||||
}
|
||||
|
||||
private async getBsipaInstalled(version: BSVersion): Promise<BbmModVersion> {
|
||||
@@ -284,11 +290,10 @@ export class BsModsManagerService {
|
||||
|
||||
const contentPath = path.join(ipaPath, content.name);
|
||||
|
||||
const res = await tryit(() => content.isDirectory() ? (
|
||||
rm(contentPath, { force: true, recursive: true })
|
||||
) : (
|
||||
unlink(contentPath)
|
||||
));
|
||||
const res = await tryit(() => content.isDirectory()
|
||||
? deleteFolder(contentPath)
|
||||
: deleteFile(contentPath)
|
||||
);
|
||||
|
||||
if(res.error){
|
||||
log.error("Error while clearing IPA folder content", content.name, res.error);
|
||||
@@ -312,7 +317,7 @@ export class BsModsManagerService {
|
||||
|
||||
const promises = mod.version.contentHashes.map(content => {
|
||||
const file = content.path.replaceAll("IPA/", "").replaceAll("Data", "Beat Saber_Data");
|
||||
return unlinkPath(path.join(verionPath, file));
|
||||
return deleteFile(path.join(verionPath, file));
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
@@ -325,7 +330,10 @@ export class BsModsManagerService {
|
||||
const versionPath = await this.bsLocalService.getVersionPath(version);
|
||||
|
||||
const promises = mod.version.contentHashes.map(async content => {
|
||||
return Promise.all([unlinkPath(path.join(versionPath, content.path)), unlinkPath(path.join(versionPath, "IPA", "Pending", content.path))]);
|
||||
return Promise.all([
|
||||
deleteFile(path.join(versionPath, content.path)),
|
||||
deleteFile(path.join(versionPath, "IPA", "Pending", content.path))
|
||||
]);
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
@@ -342,8 +350,8 @@ export class BsModsManagerService {
|
||||
|
||||
const bsipa = await this.getBsipaInstalled(version);
|
||||
|
||||
const pluginsMods = await Promise.all([this.getModsInDir(version, ModsInstallFolder.PLUGINS), this.getModsInDir(version, ModsInstallFolder.PLUGINS_PENDING)]);
|
||||
const libsMods = await Promise.all([this.getModsInDir(version, ModsInstallFolder.LIBS), this.getModsInDir(version, ModsInstallFolder.LIBS_PENDING)]);
|
||||
const pluginsMods = await Promise.all([this.getModsInDir(version, ModsInstallFolder.PLUGINS_PENDING), this.getModsInDir(version, ModsInstallFolder.PLUGINS)]);
|
||||
const libsMods = await Promise.all([this.getModsInDir(version, ModsInstallFolder.LIBS_PENDING), this.getModsInDir(version, ModsInstallFolder.LIBS)]);
|
||||
|
||||
const dirMods = pluginsMods.flat().concat(libsMods.flat());
|
||||
|
||||
@@ -531,9 +539,11 @@ export class BsModsManagerService {
|
||||
const versionMods = await this.getAvailableMods(version);
|
||||
const installedMods = await this.getInstalledMods(version);
|
||||
|
||||
const fullInstalledMods: BbmFullMod[] = installedMods?.map(version => {
|
||||
return { version, mod: versionMods.find(mod => version.modId === mod.mod.id)?.mod };
|
||||
}) ?? [];
|
||||
const fullInstalledMods = (installedMods || []).reduce((mods, version) => {
|
||||
const mod = versionMods.find(mod => version.modId === mod.mod.id)?.mod;
|
||||
if (mod) { mods.push({ version, mod }); }
|
||||
return mods;
|
||||
}, [] as BbmFullMod[]);
|
||||
|
||||
const progress = { current: 0, total: fullInstalledMods.length };
|
||||
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
import { createWriteStream, WriteStream } from 'fs';
|
||||
import { Progression } from 'main/helpers/fs.helpers';
|
||||
import { deleteFileSync, Progression } from 'main/helpers/fs.helpers';
|
||||
import { Observable } from 'rxjs';
|
||||
import { shareReplay, tap } from 'rxjs/operators';
|
||||
import log from 'electron-log';
|
||||
import got from 'got';
|
||||
import { IncomingHttpHeaders, IncomingMessage } from 'http';
|
||||
import { unlinkSync } from 'fs-extra';
|
||||
import { tryit } from 'shared/helpers/error.helpers';
|
||||
import path from 'path';
|
||||
import { pipeline } from 'stream/promises';
|
||||
import sanitize from 'sanitize-filename';
|
||||
import internal from 'stream';
|
||||
import { app } from 'electron';
|
||||
|
||||
export class RequestService {
|
||||
private static instance: RequestService;
|
||||
private preferredFamily: number | undefined = undefined;
|
||||
private readonly baseHeaders = {
|
||||
'User-Agent': `BSManager/${app.getVersion()} (Electron/${process.versions.electron} Chrome/${process.versions.chrome} Node/${process.versions.node})`,
|
||||
}
|
||||
|
||||
|
||||
public static getInstance(): RequestService {
|
||||
if (!RequestService.instance) {
|
||||
RequestService.instance = new RequestService();
|
||||
}
|
||||
return RequestService.instance;
|
||||
|
||||
}
|
||||
|
||||
private constructor() {}
|
||||
@@ -33,7 +38,7 @@ export class RequestService {
|
||||
try {
|
||||
|
||||
// @ts-ignore (ESM is not well supported in this project, We need to move out electron-react-boilerplate, and use Vite)
|
||||
const res = await got(url, { dnsLookupIpVersion: family, responseType: 'json' });
|
||||
const res = await got(url, { dnsLookupIpVersion: family, responseType: 'json', headers: this.baseHeaders });
|
||||
this.preferredFamily = family;
|
||||
return { data: res.body as T, headers: res.headers };
|
||||
} catch (err) {
|
||||
@@ -68,7 +73,7 @@ export class RequestService {
|
||||
let file: WriteStream | undefined;
|
||||
|
||||
// @ts-ignore (ESM is not well supported in this project, We need to move out electron-react-boilerplate, and use Vite)
|
||||
stream = got.stream(url, { dnsLookupIpVersion: family });
|
||||
stream = got.stream(url, { dnsLookupIpVersion: family, headers: this.baseHeaders });
|
||||
|
||||
stream.on('response', (response) => {
|
||||
this.preferredFamily = family;
|
||||
@@ -84,7 +89,7 @@ export class RequestService {
|
||||
|
||||
pipeline(stream, file).catch(err => {
|
||||
file?.destroy();
|
||||
tryit(() => unlinkSync(dest));
|
||||
tryit(() => deleteFileSync(dest));
|
||||
subscriber.error(err);
|
||||
});
|
||||
});
|
||||
@@ -131,6 +136,7 @@ export class RequestService {
|
||||
data: null,
|
||||
};
|
||||
|
||||
const headers = { ...this.baseHeaders, ...(options?.headers ?? {}) };
|
||||
const familiesToTry = this.preferredFamily ? [this.preferredFamily, this.preferredFamily === 4 ? 6 : 4] : [4, 6];
|
||||
|
||||
let attempt = 0;
|
||||
@@ -143,9 +149,8 @@ export class RequestService {
|
||||
}
|
||||
|
||||
const family = familiesToTry[attempt++];
|
||||
|
||||
// @ts-ignore (ESM is not well supported in this project, We need to move out electron-react-boilerplate, and use Vite)
|
||||
stream = got.stream(url, { dnsLookupIpVersion: family, ...(options ?? {}) });
|
||||
stream = got.stream(url, { dnsLookupIpVersion: family, ...(options ?? {}), headers });
|
||||
|
||||
let data = Buffer.alloc(0);
|
||||
let response: IncomingMessage;
|
||||
|
||||
@@ -17,7 +17,7 @@ import { PlaylistsManagerService } from "renderer/services/playlists-manager.ser
|
||||
import { MapsManagerService } from "renderer/services/maps-manager.service";
|
||||
import { map } from "rxjs";
|
||||
|
||||
export const LinkContentModal: ModalComponent<boolean, {version: BSVersion, contentType: "maps"|"playlists"|"avatars"|"sabers"|"platforms"|"blocks"}> = ({options: { data: { version, contentType } }, resolver }) => {
|
||||
export const LinkContentModal: ModalComponent<void, {version: BSVersion, contentType: "maps"|"playlists"|"avatars"|"sabers"|"platforms"|"blocks"}> = ({options: { data: { version, contentType } }, resolver }) => {
|
||||
const { text: t, element: te } = useTranslationV2();
|
||||
|
||||
const versionLinker = useService(VersionFolderLinkerService);
|
||||
|
||||
@@ -81,7 +81,7 @@ export class ModelsManagerService {
|
||||
return this.versionFolderLinked.linkVersionFolder({
|
||||
version,
|
||||
relativeFolder: MODEL_TYPE_FOLDERS[type],
|
||||
options: { keepContents: res.data !== false },
|
||||
options: { keepContents: true },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ export class PlaylistsManagerService {
|
||||
return this.linker.linkVersionFolder({
|
||||
version,
|
||||
relativeFolder: PlaylistsManagerService.RELATIVE_PLAYLISTS_FOLDER,
|
||||
options: { keepContents: !!modalRes.data },
|
||||
options: { keepContents: true },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||