Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3bddc0e54 | ||
|
|
8578063b0a | ||
|
|
27be876501 | ||
|
|
ab2721716a | ||
|
|
f26c591572 | ||
|
|
4d70fa086d | ||
|
|
f75a8efd8d | ||
|
|
10487caa30 | ||
|
|
6bf96a5f3a | ||
|
|
1a8b19ce43 | ||
|
|
6e2f0a0872 | ||
|
|
6d8b66cf52 | ||
|
|
8e2385448b | ||
|
|
d6de8b34a1 | ||
|
|
ad6a3b70aa | ||
|
|
782492857f | ||
|
|
f7c293084c | ||
|
|
90fdca1d1e | ||
|
|
b9b09778ad | ||
|
|
7f2833c4ac | ||
|
|
984f5a66d3 | ||
|
|
85af1a555f | ||
|
|
3ece7f30b4 | ||
|
|
5fffc3d48a | ||
|
|
133191685f | ||
|
|
b6013321ba | ||
|
|
fe46619a57 | ||
|
|
e7f4d6c0b3 | ||
|
|
3df1b699c5 | ||
|
|
ec409a5c93 | ||
|
|
c5bde3cde6 | ||
|
|
cb3a220254 |
@@ -1,21 +0,0 @@
|
||||
env:
|
||||
browser: true
|
||||
es2021: true
|
||||
node: true
|
||||
extends: eslint:recommended
|
||||
parserOptions:
|
||||
ecmaVersion: latest
|
||||
sourceType: module
|
||||
rules:
|
||||
indent:
|
||||
- error
|
||||
- tab
|
||||
linebreak-style:
|
||||
- error
|
||||
- unix
|
||||
quotes:
|
||||
- error
|
||||
- double
|
||||
semi:
|
||||
- error
|
||||
- never
|
||||
2
.github/FUNDING.yml
vendored
@@ -1 +1 @@
|
||||
liberapay: barbapapa
|
||||
custom: https://paypal.me/onderkin
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report a bug in this project
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of the bug.
|
||||
|
||||
**Is your bug report related to another bug report? Please describe.**
|
||||
If yes, the issue number and an explanation of why the bug is related to this one.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the bug report here.
|
||||
23
.github/ISSUE_TEMPLATE/download_problem.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Download problem
|
||||
about: Report a difficulty in downloading a game
|
||||
title: ''
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the game you are trying to download**
|
||||
A clear and concise description of the bug.
|
||||
**App ID:** <app id>
|
||||
**Depot ID:** <depot id>
|
||||
**Manifest ID:** <manifest id>
|
||||
|
||||
**Show the error that DepotDownloader produces**
|
||||
A screenshot or a copy-paste wrapped in a code-block (` ``` `) of the DepotDownloader terminal output
|
||||
|
||||
**Do you own the game?**
|
||||
If no, you probably can't download the game. You must own the game on Steam to be able to download it.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the issue here.
|
||||
10
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -2,19 +2,19 @@
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
21
.github/dependabot.yml
vendored
@@ -3,4 +3,23 @@ updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
interval: "monthly"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-major"]
|
||||
groups:
|
||||
npm-deps:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "src-tauri/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-major"]
|
||||
groups:
|
||||
cargo-deps:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
45
.github/workflows/build.yml
vendored
@@ -1,45 +0,0 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Check out repositoty
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up nodejs
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Set up npm project
|
||||
run: npm install
|
||||
|
||||
- name: Build project
|
||||
run: npm run buildall
|
||||
|
||||
- name: Move x64 exe
|
||||
run: cp ./dist/SteamDepotDownloaderGUI*.exe ./SteamDepotDownloaderGUI-${{ github.run_number }}.exe
|
||||
|
||||
- name: Move x64 AppImage
|
||||
run: cp `ls -d1 dist/* | grep -E "SteamDepotDownloaderGUI-[0-9]+\.[0-9]+\.[0-9]+\.AppImage"` ./SteamDepotDownloaderGUI-${{ github.run_number }}.AppImage
|
||||
|
||||
- name: Move x64 zip
|
||||
run: cp `ls -d1 dist/* | grep -E "steamdepotdownloadergui-[0-9]+\.[0-9]+\.[0-9]+\.zip"` ./SteamDepotDownloaderGUI-${{ github.run_number }}.zip
|
||||
|
||||
- name: Move arm64 AppImage
|
||||
run: cp `ls -d1 dist/* | grep -E "SteamDepotDownloaderGUI-[0-9]+\.[0-9]+\.[0-9]+\-arm64.AppImage"` ./SteamDepotDownloaderGUI-${{ github.run_number }}.AppImage
|
||||
|
||||
- name: Move arm64 zip
|
||||
run: cp `ls -d1 dist/* | grep -E "steamdepotdownloadergui-[0-9]+\.[0-9]+\.[0-9]+\-arm64.zip"` ./SteamDepotDownloaderGUI-${{ github.run_number }}-arm64.zip
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: SteamDepotDownloaderGUI-${{ github.run_number }}
|
||||
path: SteamDepotDownloaderGUI-${{ github.run_number }}*.*
|
||||
19
.github/workflows/github-repo-stats.yml
vendored
@@ -1,19 +0,0 @@
|
||||
name: github-repo-stats
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run this once per day, towards the end of the day for keeping the most
|
||||
# recent data point most meaningful (hours are interpreted in UTC).
|
||||
- cron: "0 23 * * *"
|
||||
workflow_dispatch: # Allow for running this manually.
|
||||
|
||||
jobs:
|
||||
j1:
|
||||
name: github-repo-stats
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: run-ghrs
|
||||
# Use latest release.
|
||||
uses: jgehrcke/github-repo-stats@v1.4.1 # Don't use latest release for security reasons (what if the workflow gets taken over by a malicious party)
|
||||
with:
|
||||
ghtoken: ${{ secrets.ghrs_github_api_token }}
|
||||
153
.gitignore
vendored
@@ -4,140 +4,29 @@ logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
*.iml
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# VSCode directory for storing user settings
|
||||
.vscode
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
|
||||
# SteamDepotDownloaderGUI specific
|
||||
depotdownloader
|
||||
games
|
||||
*.zip
|
||||
run.bat
|
||||
run.sh
|
||||
# SteamDepotDownloaderGUI files
|
||||
src-tauri/depotdownloader
|
||||
src-tauri/*.zip
|
||||
src-tauri/*.exe
|
||||
**/DepotDownloader
|
||||
**/DepotDownloader.xml
|
||||
6
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"tauri-apps.tauri-vscode",
|
||||
"rust-lang.rust-analyzer"
|
||||
]
|
||||
}
|
||||
144
README.md
@@ -1,109 +1,99 @@
|
||||
<div align="center">
|
||||
<h1>SteamDepotDownloaderGUI</h1>
|
||||
<h4>A graphical wrapper for DepotDownloader, designed to make downloading older versions of Steam games easy.</h4>
|
||||
|
||||
<h1 align="center">SteamDepotDownloaderGUI
|
||||
|
||||
<h4 align="center">A graphical wrapper for DepotDownloader, designed to make downloading older versions of Steam games easy. built using <a href="https://www.electronjs.org" target="_blank">Electron</a>.
|
||||
<br><br>
|
||||
→<a href="https://www.youtube.com/watch?v=H2COwT5OUOo" target="_blank"><b>Tutorial</b></a> ~
|
||||
<a href="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/tree/steamdeck-dotnet#steamdepotdownloadergui-for-steam-deck" target="_blank"><b>Steam Deck</b></a> ~
|
||||
<a href="https://www.youtube.com/watch?v=ogiDAuH3VdY" target="_blank"><b>Subnautica</b></a>←
|
||||
→ <a href="https://www.youtube.com/watch?v=H2COwT5OUOo" target="_blank"><b>Tutorial</b></a> ~
|
||||
<a href="https://depotdownloader.00pium.net/" target="_blank"><b>Website</b></a> ~
|
||||
<a href="https://www.youtube.com/watch?v=ogiDAuH3VdY" target="_blank"><b>Example usage</b></a> ←
|
||||
</h4>
|
||||
|
||||
|
||||
<a href="https://img.shields.io/github/last-commit/mmvanheusden/SteamDepotDownloaderGUI?color=crimson"><img src="https://img.shields.io/github/last-commit/mmvanheusden/SteamDepotDownloaderGUI?color=crimson" alt="Last contribution badge"></a>
|
||||
<a href="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest"><img src="https://img.shields.io/badge/Download -ffbd03?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAABd0lEQVRoge2XwUrDQBCGPz3Ee++2UBSvdsGbeLHeFX0lPQhei+8g2AfwSVREK+LJ1oqHUGjRQ7Z0laRJNtldKPPBwoTM7D9/sks2IAiCsKrsAK/AC7DtSmTd1cTAKbAJtIAzVyIuDURGvOFKxKUBL4iB0IiB0IiB0IiB0IiB0IiB0IiB0IgBoA08Ak+AKpCvdO6AGn416zBwDGyRGLljuQmlc9pAEzipKl6HgVtgpOMGSYOdlLyOvtfQ1yPgpgb9WlDAEPjRYwj0jet+yv0iy80ru8AHiyazxiewF6jHXPJMeG3+CoiBy5J1/5dTlWVzAXwD5yXrAJho4YlFbdqesFnzsa6PLWr/PD0bFPAA3GO/YXN7WMspLpLnktweVvooMTPiKDPLHabmLCtpmYF3I96v3E55Doz4zWaCaxYb6Bno4udNRMCR1pzr92wmagFfZH+UfI0xycHPim5gE2Pg0Lb5OU2SVzgAph6anmqtHhWevCAIgh9+AdLMtu/CZhHJAAAAAElFTkSuQmCC" alt="Download latest release badge"></a>
|
||||
<a href="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest"><img src="https://img.shields.io/github/downloads/mmvanheusden/SteamDepotDownloaderGUI/total?color=orange&label=downloads" alt="Download count badge"></a>
|
||||
|
||||
<p align="center"><a href="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest"><img src="https://img.shields.io/github/downloads/mmvanheusden/SteamDepotDownloaderGUI/total?color=orange&label=downloads" alt="Download count badge"></a><a href="https://img.shields.io/github/last-commit/mmvanheusden/SteamDepotDownloaderGUI?color=crimson"><img src="https://img.shields.io/github/last-commit/mmvanheusden/SteamDepotDownloaderGUI?color=crimson" alt="Last contribution badge"></a><a href="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest"><img src="https://img.shields.io/badge/Download -ffbd03?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABmJLR0QA/wD/AP+gvaeTAAABd0lEQVRoge2XwUrDQBCGPz3Ee++2UBSvdsGbeLHeFX0lPQhei+8g2AfwSVREK+LJ1oqHUGjRQ7Z0laRJNtldKPPBwoTM7D9/sks2IAiCsKrsAK/AC7DtSmTd1cTAKbAJtIAzVyIuDURGvOFKxKUBL4iB0IiB0IiB0IiB0IiB0IiB0IiB0IgBoA08Ak+AKpCvdO6AGn416zBwDGyRGLljuQmlc9pAEzipKl6HgVtgpOMGSYOdlLyOvtfQ1yPgpgb9WlDAEPjRYwj0jet+yv0iy80ru8AHiyazxiewF6jHXPJMeG3+CoiBy5J1/5dTlWVzAXwD5yXrAJho4YlFbdqesFnzsa6PLWr/PD0bFPAA3GO/YXN7WMspLpLnktweVvooMTPiKDPLHabmLCtpmYF3I96v3E55Doz4zWaCaxYb6Bno4udNRMCR1pzr92wmagFfZH+UfI0xycHPim5gE2Pg0Lb5OU2SVzgAph6anmqtHhWevCAIgh9+AdLMtu/CZhHJAAAAAElFTkSuQmCC" alt="Download latest release badge"></a><a href="https://img.shields.io/endpoint?url=https%3A%2F%2Fhits.dwyl.com%2Fmmvanheusden%2Fsteamdepotdownloadergui.json&color=lightblue"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fhits.dwyl.com%2Fmmvanheusden%2Fsteamdepotdownloadergui.json&color=lightblue" alt="Visitor count badge"></a><a href="https://liberapay.com/barbapapa"><img src="https://img.shields.io/badge/Donate-black?logo=liberapay" alt="Donation badge"></a><a href="https://www.codefactor.io/repository/github/mmvanheusden/steamdepotdownloadergui/badge/master"><img src="https://www.codefactor.io/repository/github/mmvanheusden/steamdepotdownloadergui/badge/master" alt="CodeFactor badge"></a></p>
|
||||
<img src="https://github.com/user-attachments/assets/2e1b1b8e-9560-4dde-86c0-b70384a54fbb" alt="Steam downgrader interface" style="max-width: 40%;"/>
|
||||
</div>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img alt="Screenshot of the downloader interface" src="screenshot.png"
|
||||
style="max-width: 60%;"/>
|
||||
</p>
|
||||
|
||||
## Features
|
||||
- **Cross-platform support**
|
||||
| OS | Supported |
|
||||
|---------|-----------|
|
||||
| Windows | ✅ |
|
||||
| Linux | ✅ |
|
||||
| macOS | ✅ |
|
||||
|
||||
* **Cross platform**
|
||||
- Windows
|
||||
- <s>macOS</s> (in development™)
|
||||
- Linux
|
||||
- [Steam Deck](https://github.com/mmvanheusden/SteamDepotDownloaderGUI/tree/steamdeck-dotnet#steamdepotdownloadergui-for-steam-deck)
|
||||
* **Cross terminal**
|
||||
- Gnome Terminal
|
||||
- Konsole
|
||||
- xfce4-terminal
|
||||
- Terminator
|
||||
- Terminology
|
||||
- xterm
|
||||
- Kitty
|
||||
- LXTerminal
|
||||
- Tilix
|
||||
- Deepin Terminal
|
||||
- cool-retro-term
|
||||
- Manual: produce a script that can be copy pasted into a terminal of choice
|
||||
* **Support for anonymous downloads**
|
||||
- **Support for every major Linux terminal emulator**
|
||||
<details><summary>List of supported terminals</summary>
|
||||
|
||||
* GNOME Terminal
|
||||
* GNOME Console
|
||||
* Konsole
|
||||
* Xfce-terminal
|
||||
* Alacritty
|
||||
* XTerm
|
||||
* Terminator
|
||||
* cool-retro-term
|
||||
* Kitty
|
||||
* LXTerminal
|
||||
* Deepin Terminal
|
||||
* Terminology
|
||||
* Tilix
|
||||
</details>
|
||||
|
||||
- **Automatic download and extraction of DepotDownloader**
|
||||
|
||||
|
||||
## How to download
|
||||
> [!CAUTION]
|
||||
> This GitHub repository is the only official place to download this software.
|
||||
> If you have paid for this software, or downloaded this from an untrusted place, **you are at risk** <sub><sup><sub><sup>and an idiot.<sub><sup><sub><sup>
|
||||
|
||||
## Download
|
||||
|
||||
### Windows:
|
||||
* If you haven't already, install [.NET SDK 6.0](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) on your computer.
|
||||
Then, download the [latest Windows build](https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest)
|
||||
### **Linux:**
|
||||
#### Arch Linux:
|
||||
* An official [AUR package](https://aur.archlinux.org/packages/steamdepotdownloadergui-appimage) has been made. this can be installed using `yay -S steamdepotdownloadergui-appimage`.
|
||||
#### Other Linux distros:
|
||||
* **You'll need `unzip`, .NET SDK 6.0.**
|
||||
* **Ubuntu:**
|
||||
|
||||
`sudo apt install unzip dotnet-sdk-6.0`
|
||||
* **Fedora:**
|
||||
|
||||
`sudo dnf install unzip dotnet-sdk-6.0`
|
||||
|
||||
|
||||
* There are several options, AppImage, and a `.zip`, for both x64 and arm64.
|
||||
|
||||
[Download latest release](https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest)
|
||||
Download the [latest Windows release](https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest). There are multiple variants to choose from, but you are probably looking for the file that ends with **`.exe`**.
|
||||
|
||||
|
||||
## How to use
|
||||
### Linux:
|
||||
You'll need at least one of the supported terminal emulators. You most likely already have one of these.
|
||||
|
||||
Download the [latest Linux release](https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/latest). There are multiple options to choose from.
|
||||
|
||||
|
||||
## Tutorials
|
||||
* https://www.youtube.com/watch?v=H2COwT5OUOo How to download older versions of Steam games tutorial
|
||||
|
||||
#### Tutorials:
|
||||
* https://www.youtube.com/watch?v=H2COwT5OUOo How to download older versions of Steam games. rollback steam games tutorial
|
||||
* https://www.youtube.com/watch?v=ogiDAuH3VdY How to download older versions of Subnautica tutorial
|
||||
---
|
||||
|
||||
**Enter everything you normally would in the DepotDownloader console and select your OS/terminal and click download**
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
This software makes use of the following projects:
|
||||
- [**DepotDownloader**](https://github.com/SteamRE/DepotDownloader/)
|
||||
- [Electron](https://www.electronjs.org/)
|
||||
- [Node.js](https://nodejs.org/)
|
||||
- [Tauri](https://tauri.app)
|
||||
- [Primer CSS](https://primer.style/css/)
|
||||
- [follow-redirects](https://github.com/follow-redirects/follow-redirects)
|
||||
- [async-process](https://github.com/smol-rs/async-process)
|
||||
- [Hubut Sans](https://github.com/github/hubot-sans) under [license](https://github.com/github/hubot-sans/blob/05d5ea150c20e6434485db8ffd2277ed18a9e911/LICENSE)
|
||||
|
||||
|
||||
## Donate
|
||||
You can donate [here](paypal.me/onderkin) or through the **donate** button in the interface.
|
||||
|
||||
Donations can be made through Liberapay :)
|
||||
|
||||
<a href="https://liberapay.com/barbapapa">
|
||||
<img src="https://img.shields.io/badge/Donate-black?logo=liberapay&style=for-the-badge"
|
||||
alt="Donation badge">
|
||||
</a>
|
||||
|
||||
## On Electron
|
||||
Computer resources are not an all-you-can-eat buffet. The executables weigh on average 103 MB.
|
||||
That's why in the future, this project is fully switching to a [different](https://tauri.app/) (multi-platform) toolset. Nothing will change for the user.
|
||||
|
||||
## Contribute
|
||||
Every pull request is welcome! ;)
|
||||
Please cleanup the code using:
|
||||
```console
|
||||
$ pnpm eslint --fix src/
|
||||
```
|
||||
|
||||
If you would like to contribute to SteamDepotDownloaderGUI, please make sure to follow
|
||||
the [contributing instructions and guidelines](contributing.md).
|
||||
|
||||
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
||||
<p align="center">
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0" target="_blank">
|
||||
<img src="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/assets/50550545/b5649b7f-ea49-45c4-b0cd-5f3788dcd6ca" height="40px">
|
||||
</a>
|
||||
<a href="https://00pium.net" target="_blank">
|
||||
<img src="https://github.com/mmvanheusden/SteamDepotDownloaderGUI/assets/50550545/83f5f3b2-2bf9-41aa-ab87-880466f785fe" height="40px">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
Please make sure to keep code clean and cross-platform compatible.
|
||||
|
||||

|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
# SteamDepotDownloaderGUI development information
|
||||
|
||||
SteamDepotDownloaderGUI is a graphical user interface for DepotDownloader.
|
||||
|
||||
It is written in Node.js and uses the [Electron](https://electronjs.org/) framework.
|
||||
The CSS framework being used is [Primer](https://primer.style), by GitHub.
|
||||
|
||||
## Setup development environment
|
||||
|
||||
**Note that these instructions may differ from operating system to operating system.**
|
||||
|
||||
* Install node.js, npm, and git on your computer.
|
||||
|
||||
* Clone the repository.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/mmvanheusden/SteamDepotDownloaderGUI
|
||||
```
|
||||
|
||||
* Move into the directory and install npm dependencies.
|
||||
|
||||
```bash
|
||||
cd SteamDepotDownloaderGUI
|
||||
npm install
|
||||
```
|
||||
|
||||
* **You are now ready to contribute**
|
||||
|
||||
## Testing the application
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
## Building the application
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
* If you are using macOS, you can build using the following command:
|
||||
|
||||
```bash
|
||||
npm run buildall
|
||||
```
|
||||
|
||||
This will build the application for all supported operating systems.
|
||||
|
||||
## Contributing Guidelines
|
||||
|
||||
Please make sure to keep code consistent and cross-platform compatible.
|
||||
|
||||
After you made a change, clean-up the code using your favorite code editor and run the following command:
|
||||
|
||||
```bash
|
||||
npx eslint *.js --fix
|
||||
```
|
||||
|
||||
Please make sure your git commit message is descriptive and contains the changes you made.
|
||||
|
||||
**Happy coding!**
|
||||
358
downloader.js
@@ -1,358 +0,0 @@
|
||||
// Uses a prebuild binary of https://github.com/SteamRE/DepotDownloader
|
||||
// License can be found at https://github.com/SteamRE/DepotDownloader/blob/master/LICENSE
|
||||
const {ipcRenderer, shell} = require("electron")
|
||||
const {
|
||||
preDownloadCheck,
|
||||
download,
|
||||
runCommand,
|
||||
removeDir,
|
||||
removeFile,
|
||||
unzip,
|
||||
forceTerminals,
|
||||
generateRunScript,
|
||||
createCommandWithTerminal,
|
||||
platformpath
|
||||
} = require("./utils")
|
||||
const electron = require("electron")
|
||||
const {sep} = require("path")
|
||||
|
||||
// Initializes the variable that holds the path to the specified download location
|
||||
let exportedFile = ""
|
||||
let ready = true
|
||||
let app_version
|
||||
|
||||
const DOTNET_DOWNLOAD_URL = "https://github.com/SteamRE/DepotDownloader/releases/download/DepotDownloader_2.5.0/depotdownloader-2.5.0.zip" // the url to the depotdownloader zip
|
||||
const DOTNET_DIR = "depotdownloader" // folder where zip is extracted
|
||||
const DOTNET_ZIP_FILE = DOTNET_DOWNLOAD_URL.split("/").pop() // the file that is being downloaded.
|
||||
|
||||
function submitForm() {
|
||||
// Check if the form is filled in and if dotnet is installed
|
||||
preDownloadCheck().then(async function () {
|
||||
//console.log(terminal[1][0])
|
||||
document.getElementById("dotnetwarning").hidden = true
|
||||
document.getElementById("dotnetwarning2").hidden = true
|
||||
document.getElementById("emptywarning").hidden = true
|
||||
console.info("dotnet found in PATH")
|
||||
// create variables for the form values
|
||||
const username = document.getElementById("username").value
|
||||
const password = document.getElementById("password").value
|
||||
const appid = document.getElementById("appid").value
|
||||
const depotid = document.getElementById("depotid").value
|
||||
const manifestid = document.getElementById("manifestid").value
|
||||
const terminal_dropdown = document.getElementById("terminal-dropdown").selectedIndex
|
||||
const os_dropdown = document.getElementById("os-dropdown").selectedIndex
|
||||
const foldername_element = document.getElementById("folder-name-custom-input")
|
||||
|
||||
|
||||
// Remove the old depotdownloader directory if there are any
|
||||
await removeDir("depotdownloader")
|
||||
|
||||
// Download a prebuild DepotDownloader binary, so it doesn't have to be included in the source code
|
||||
await download(DOTNET_DOWNLOAD_URL)
|
||||
|
||||
// Unzip the DepotDownloader binary
|
||||
await unzip(DOTNET_ZIP_FILE, DOTNET_DIR)
|
||||
|
||||
// Clean up the old files
|
||||
await removeFile(DOTNET_ZIP_FILE)
|
||||
|
||||
// Generate the run script (run.sh for linux, or run.bat for windows).
|
||||
await generateRunScript(username, password, appid, depotid, manifestid, foldername_element, exportedFile)
|
||||
|
||||
let terminal
|
||||
let os
|
||||
if (terminal_dropdown === 11) {
|
||||
terminal = "auto"
|
||||
} else {
|
||||
terminal = terminal_dropdown
|
||||
}
|
||||
if (os_dropdown === 4) {
|
||||
os = "auto"
|
||||
} else {
|
||||
os = os_dropdown
|
||||
}
|
||||
let command
|
||||
if (process.platform.includes("linux")) {
|
||||
// if the OS is linux, run the sh file with the chosen terminal
|
||||
command = await createCommandWithTerminal(`sh ${platformpath().replaceAll(" ", "\\ ")}${sep}run.sh`, terminal, os)
|
||||
} else if (process.platform.includes("win")) {
|
||||
// if the OS is windows, just run the batch file
|
||||
command = await createCommandWithTerminal(`"${platformpath()}${sep}run.bat"`, terminal, os)
|
||||
} else if (process.platform.includes("darwin")) {
|
||||
//macOS
|
||||
}
|
||||
|
||||
// Run the final command
|
||||
if (document.getElementById("os-dropdown").selectedIndex !== 3) await console.debug("Command issued:\n--------\n" + command)
|
||||
await runCommand(command)
|
||||
}).catch(function (error) {
|
||||
if (error === "noDotnet") {
|
||||
// if dotnet is not found, show the dotnet warning
|
||||
document.getElementById("dotnetwarning2").hidden = true
|
||||
document.getElementById("emptywarning").hidden = true
|
||||
document.getElementById("dotnetwarning").hidden = false
|
||||
} else if (error === "emptyField") {
|
||||
// if a required field is empty, show the empty field warning
|
||||
document.getElementById("dotnetwarning2").hidden = true
|
||||
document.getElementById("dotnetwarning").hidden = true
|
||||
document.getElementById("emptywarning").hidden = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Combines all buttons that are supposed to open an external URL into one big function.
|
||||
function openRelevantPage(target) {
|
||||
const electron = require("electron")
|
||||
const os = process.platform.toString()
|
||||
/* eslint-disable indent */
|
||||
// why are you not indenting nicely eslint?
|
||||
switch (target) {
|
||||
case "dotnet":
|
||||
document.getElementById("dotnetwarning").hidden = true
|
||||
document.getElementById("dotnetwarning2").hidden = false
|
||||
if (os.includes("win")) {
|
||||
console.debug("Opened .NET download page for " + os.charAt(0).toUpperCase() + os.slice(1))
|
||||
void electron.shell.openExternal("https://aka.ms/dotnet/6.0/dotnet-sdk-win-x64.exe")
|
||||
}
|
||||
if (os.includes("linux")) {
|
||||
const electron = require("electron")
|
||||
console.debug("Opened .NET download page for " + os.charAt(0).toUpperCase() + os.slice(1))
|
||||
void electron.shell.openExternal("https://docs.microsoft.com/en-us/dotnet/core/install/linux")
|
||||
}
|
||||
if (os.includes("darwin")) {
|
||||
console.debug("Opened .NET download page for" + os)
|
||||
//TODO: Apple Silicon(ARM64) URL
|
||||
void electron.shell.openExternal("https://aka.ms/dotnet/6.0/dotnet-sdk-osx-x64.pkg")
|
||||
}
|
||||
break
|
||||
case "issues":
|
||||
console.debug("Opened GitHub issues page")
|
||||
void electron.shell.openExternal("https://github.com/mmvanheusden/SteamDepotDownloaderGUI/issues/new")
|
||||
break
|
||||
case "steamdb":
|
||||
console.debug("Opened SteamDB instant search page")
|
||||
void electron.shell.openExternal("https://steamdb.info/instantsearch/")
|
||||
break
|
||||
case "donate":
|
||||
console.debug("Opened donation page")
|
||||
void electron.shell.openExternal("https://liberapay.com/barbapapa/")
|
||||
break
|
||||
case "instructions":
|
||||
console.debug("Opened instructions page")
|
||||
void electron.shell.openExternal("https://github.com/mmvanheusden/SteamDepotDownloaderGUI/#how-to-use")
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
/* eslint-enable indent */
|
||||
}
|
||||
|
||||
// Opens the chosen location where the game will be downloaded to
|
||||
function checkPath() {
|
||||
toggleFormAccessibility(false)
|
||||
shell.openPath(exportedFile).then(() => {
|
||||
console.log("Opened " + exportedFile + " in file explorer.")
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the values for default os
|
||||
* Runs when the app is fully loaded.
|
||||
*/
|
||||
function fillDefaultValues() {
|
||||
// [0]: Windows, [1]: macOS [2]: Linux [3]: manual
|
||||
if (process.platform.toString().includes("linux")) {
|
||||
document.getElementById("default-os").innerText = "Linux"
|
||||
} else if (process.platform.toString().includes("win")) {
|
||||
document.getElementById("default-os").innerText = "Windows"
|
||||
} else if (process.platform.toString().includes("darwin")) {
|
||||
document.getElementById("default-os").innerText = "macOS"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validates the choice of the OS dropdown.
|
||||
* If the choice is 2 (Linux), enable the terminal selection dropdown.
|
||||
* If the choice is not 2 (Linux), disable the terminal selection dropdown.
|
||||
* If the choice is 4 (manual), enable the terminal selection dropdown.
|
||||
* If the choice is not 4 (manual), disable the terminal selection dropdown.
|
||||
*/
|
||||
function validateChoice() {
|
||||
// [0]: Windows, [1]: macOS [2]: Linux [3]: manual
|
||||
const os_dropdown = document.getElementById("os-dropdown")
|
||||
const terminal_dropdown = document.getElementById("terminal-dropdown")
|
||||
// if the choice = 2, enable the terminal selection dropdown.
|
||||
if (os_dropdown.selectedIndex === (2 || 3)) {
|
||||
terminal_dropdown.disabled = false
|
||||
} else {
|
||||
terminal_dropdown.disabled = true
|
||||
terminal_dropdown.selectedIndex = 11
|
||||
}
|
||||
if (os_dropdown.selectedIndex === 4) if (process.platform.includes("linux")) terminal_dropdown.disabled = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the form.
|
||||
* @param disable Enable or disable the form.
|
||||
*
|
||||
* * true => disable everything
|
||||
*
|
||||
* * false => enable everything
|
||||
*/
|
||||
function toggleFormAccessibility(disable) {
|
||||
document.getElementById("username").disabled = disable
|
||||
document.getElementById("theform").disabled = disable
|
||||
document.getElementById("password").disabled = disable
|
||||
document.getElementById("appid").disabled = disable
|
||||
document.getElementById("depotid").disabled = disable
|
||||
document.getElementById("manifestid").disabled = disable
|
||||
document.getElementById("os-dropdown").disabled = disable
|
||||
|
||||
// if the OS dropdown value was 2 (Linux), don't disable the Terminal dropdown.
|
||||
document.getElementById("terminal-dropdown").disabled = (((document.getElementById("os-dropdown").selectedIndex !== 2)))
|
||||
|
||||
document.getElementById("pickpath").ariaDisabled = disable
|
||||
document.getElementById("pickpath").disabled = disable
|
||||
document.getElementById("downloadbtn").ariaDisabled = disable
|
||||
document.getElementById("downloadbtn").disabled = disable
|
||||
document.getElementById("settings-button").disabled = disable
|
||||
document.getElementById("downloadbtn").classList.replace(((disable) ? "btn-primary" : "btn-disabled"), ((!disable) ? "btn-primary" : "btn-disabled"))
|
||||
}
|
||||
|
||||
|
||||
function setTheme(theme) {
|
||||
document.getElementById("theme-auto").ariaSelected = (theme === "auto").toString()
|
||||
document.getElementById("theme-light").ariaSelected = (theme === "light").toString()
|
||||
document.getElementById("theme-dark").ariaSelected = (theme === "dark").toString()
|
||||
}
|
||||
|
||||
// main.js sends a ready message if the page is loaded in. This will be received here.
|
||||
ipcRenderer.on("ready", async () => {
|
||||
if (!ready) return
|
||||
|
||||
|
||||
await toggleFormAccessibility(true) // disables the form, while loading
|
||||
document.getElementById("loader").hidden = false
|
||||
|
||||
|
||||
console.debug("🐰🐰🐰🐰")
|
||||
|
||||
|
||||
await fillDefaultValues() // Set the default values based on OS
|
||||
|
||||
const terminals = await forceTerminals()
|
||||
/* forceTerminals() returns two values:
|
||||
[bol, list, list]
|
||||
1: true/false. if true, there are terminals found. if false none are, or not on linux
|
||||
2: a list of available terminals with their associated number in the terminal dropdown index.
|
||||
3: a list of available terminals with their associated name in the terminal dropdown.
|
||||
*/
|
||||
if (terminals[0]) {
|
||||
console.log(`${terminals[1].length} terminals found in PATH.`)
|
||||
document.getElementById("terminals-found").innerText = `${terminals[1].length.toString()} / ${document.getElementById("terminal-dropdown").length - 1}`
|
||||
document.getElementById("default-terminal").innerText = terminals[2][0]
|
||||
|
||||
} else {
|
||||
console.log("No terminals found in PATH. Continuing with default values") // when no terminals are found on the system, or when linux is not used.
|
||||
}
|
||||
|
||||
await toggleFormAccessibility(false) //enable the form again
|
||||
|
||||
await validateChoice() // updates the 'enabled/disabled' html value of the terminal dropdown.
|
||||
|
||||
console.debug(`DEBUGGING INFORMATION:
|
||||
--
|
||||
OS: ${process.platform}
|
||||
Version: ${app_version}
|
||||
--
|
||||
Working directory locating methods:
|
||||
process.cwd(): ${process.cwd()}
|
||||
__dirname: ${__dirname}
|
||||
PORTABLE_EXECUTABLE_DIR: ${process.env.PORTABLE_EXECUTABLE_DIR}
|
||||
--
|
||||
platformpath(): ${platformpath()}
|
||||
|
||||
`)
|
||||
document.getElementById("loader").hidden = true
|
||||
ready = false
|
||||
})
|
||||
|
||||
// Add event listeners to the buttons
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
document.getElementById("dotnetalertbtn").addEventListener("click", () => openRelevantPage("dotnet"))
|
||||
document.getElementById("smbtn1").addEventListener("click", () => openRelevantPage("issues"))
|
||||
document.getElementById("smbtn2").addEventListener("click", () => openRelevantPage("steamdb"))
|
||||
document.getElementById("smbtn3").addEventListener("click", () => openRelevantPage("donate"))
|
||||
document.getElementById("smbtn4").addEventListener("click", () => openRelevantPage("instructions"))
|
||||
document.getElementById("pickpath").addEventListener("click", () => {
|
||||
if (document.getElementById("pickpath").disabled === false) ipcRenderer.send("selectpath")
|
||||
})
|
||||
document.getElementById("checkpath").addEventListener("click", () => {
|
||||
if (document.getElementById("checkpath").disabled === false) checkPath()
|
||||
})
|
||||
document.getElementById("os-dropdown").addEventListener("input", validateChoice)
|
||||
document.getElementById("downloadbtn").addEventListener("click", () => {
|
||||
if (document.getElementById("downloadbtn").disabled === false) submitForm()
|
||||
})
|
||||
document.getElementById("settings-button").addEventListener("click", () => {
|
||||
if (document.getElementById("settings-button").disabled === false) {
|
||||
// show the modal. This is done by setting the display to block.
|
||||
document.getElementById("settings-surrounding").style.display = "block"
|
||||
}
|
||||
})
|
||||
document.getElementById("settings-surrounding").addEventListener("click", (event) => {
|
||||
if (event.target === document.getElementById("settings-surrounding")) {
|
||||
document.getElementById("settings-surrounding").style.display = "none"
|
||||
}
|
||||
})
|
||||
document.getElementById("theme-auto").addEventListener("click", () => {
|
||||
setTheme("auto")
|
||||
document.getElementById("theme").setAttribute("data-color-mode", "auto")
|
||||
})
|
||||
document.getElementById("theme-light").addEventListener("click", () => {
|
||||
setTheme("light")
|
||||
document.getElementById("theme").setAttribute("data-color-mode", "light")
|
||||
})
|
||||
document.getElementById("theme-dark").addEventListener("click", () => {
|
||||
setTheme("dark")
|
||||
document.getElementById("theme").setAttribute("data-color-mode", "dark")
|
||||
})
|
||||
document.getElementById("folder-name-appid").addEventListener("click", () => {
|
||||
document.getElementById("folder-name-custom").ariaSelected = false
|
||||
document.getElementById("folder-name-appid").ariaSelected = true
|
||||
document.getElementById("folder-name-custom-input").hidden = true
|
||||
document.getElementById("folder-name-custom-input").value = ""
|
||||
})
|
||||
document.getElementById("folder-name-custom").addEventListener("click", () => {
|
||||
document.getElementById("folder-name-appid").ariaSelected = false
|
||||
document.getElementById("folder-name-custom").ariaSelected = true
|
||||
document.getElementById("folder-name-custom-input").hidden = false
|
||||
})
|
||||
document.getElementById("version-info").addEventListener("click", () => {
|
||||
//todo: update checker
|
||||
electron.shell.openExternal(`https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/v${app_version}`)
|
||||
})
|
||||
})
|
||||
|
||||
// make sure devtools open in external window.
|
||||
window.addEventListener("keydown", (e) => {
|
||||
if (e.key === "F12" || (e.ctrlKey && e.shiftKey && e.key === "I")) {
|
||||
e.preventDefault()
|
||||
ipcRenderer.send("open-devtools")
|
||||
}
|
||||
})
|
||||
|
||||
ipcRenderer.on("file", (event, file) => {
|
||||
console.log("path selected by user: " + file)
|
||||
document.getElementById("checkpath").ariaDisabled = false // Makes the check button active
|
||||
document.getElementById("checkpath").disabled = false // Makes the check button active
|
||||
exportedFile = file.toString()
|
||||
})
|
||||
|
||||
// receive the version from main.js
|
||||
ipcRenderer.on("version", (event, version) => {
|
||||
console.log("version: " + version)
|
||||
document.getElementById("version-info").innerText = `v${version}`
|
||||
app_version = version.toString()
|
||||
})
|
||||
@@ -1,33 +0,0 @@
|
||||
appId: rocks.maarten.depotdownloader
|
||||
productName: SteamDepotDownloaderGUI
|
||||
copyright: Copyright © mmvanheusden | This work is free. Licensed under the GNU GPLv3 Public License
|
||||
|
||||
linux:
|
||||
executableName: SteamDepotDownloaderGUI
|
||||
category: Utility;FileTools;
|
||||
maintainer: mmvanheusden
|
||||
target:
|
||||
- target: AppImage
|
||||
arch:
|
||||
- x64
|
||||
- arm64
|
||||
- target: zip
|
||||
arch:
|
||||
- x64
|
||||
- arm64
|
||||
|
||||
# lmao mac building only works on mac
|
||||
mac:
|
||||
category: public.app-category.utilities
|
||||
target:
|
||||
- target: dmg
|
||||
arch:
|
||||
- x64
|
||||
|
||||
win:
|
||||
legalTrademarks: Copyright © mmvanheusden | This work is free. Licensed under the GNU GPLv3 Public License
|
||||
target:
|
||||
- target: portable
|
||||
arch:
|
||||
- x64
|
||||
- arm64
|
||||
18
eslint.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// @ts-check
|
||||
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
files: ["src/**"],
|
||||
rules: {
|
||||
"semi": ["error", "always"], // semicolons
|
||||
"indent": ["error", "tab"], // tabs indents
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"quotes": ["error", "double"]
|
||||
}
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.stylistic,
|
||||
);
|
||||
270
index.html
@@ -1,270 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- automatic dark mode :) -->
|
||||
<html data-color-mode="auto" data-dark-theme="dark" data-light-theme="light" id="theme" lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||
<link href="https://unpkg.com/@primer/css@21.1.1/dist/primer.css" rel="stylesheet"/>
|
||||
<link href="style.css" rel="stylesheet">
|
||||
<title>SteamDepotDownloaderGUI</title>
|
||||
<script src=" https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js "></script>
|
||||
</head>
|
||||
<body>
|
||||
<script src="downloader.js"></script>
|
||||
<div class="mx-auto">
|
||||
<div hidden id="dotnetwarning">
|
||||
<div class="flash flash-error mx-2 mt-2 color-shadow-medium" id="dotnetalert">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
<code><span class="text-italic">dotnet</span></code> was not found.
|
||||
<button class="btn btn-sm flash-action" id="dotnetalertbtn">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5
|
||||
0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
<span class="text-bold">Download</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden id="emptywarning">
|
||||
<div class="flash flash-warn mx-2 mt-2 color-shadow-medium" id="emptyalert">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Please fill in all required fields.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden id="dotnetwarning2">
|
||||
<div class="flash mx-2 mt-2 color-shadow-medium">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Please re-open after installing .NET.
|
||||
</div>
|
||||
</div>
|
||||
<div class="f1-light text-center">Steam Depot Downloader</div>
|
||||
|
||||
<form id="theform">
|
||||
<div class="form-group mx-3 mt-1">
|
||||
<div class="form-group-header">
|
||||
<label for="username">Username</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="username" placeholder="Leave empty for anonymous download"
|
||||
type="text"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1">
|
||||
<div class="form-group-header">
|
||||
<label for="password">Password</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="password" placeholder="Leave empty for anonymous download"
|
||||
type="password"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label for="appid">App ID</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="appid" type="number"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label for="depotid">Depot ID</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="depotid" type="number"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label for="manifestid">Manifest ID</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="manifestid" type="number"/>
|
||||
</div>
|
||||
|
||||
<div class="mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label>Download Location</label>
|
||||
</div>
|
||||
<div aria-label="Pick the path/location where the game will be downloaded to."
|
||||
class="form-control btn btn-sm tooltipped tooltipped-ne" id="pickpath">
|
||||
Set location
|
||||
</div>
|
||||
|
||||
<div aria-disabled="true"
|
||||
aria-label="Check the location that has been selected."
|
||||
class="form-control btn btn-sm ml-2 tooltipped tooltipped-ne" id="checkpath">
|
||||
Open location
|
||||
</div>
|
||||
<span class="Label mt-1 ml-3 Label--warning" id="loader">
|
||||
<span aria-label="Loading" class="tooltipped tooltipped-n">Loading</span>
|
||||
<span class="AnimatedEllipsis"></span>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<div class="form-group mt-3 ml-3 mr-3">
|
||||
<div class="BtnGroup d-flex">
|
||||
<button class="BtnGroup-item btn btn-block btn-primary flex-1" id="downloadbtn">
|
||||
<svg class="octicon filter-red" height="16"
|
||||
style="display: inline-block; user-select: none; vertical-align: text-bottom;" viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5 0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Download
|
||||
</button>
|
||||
<button class="BtnGroup-item btn flex-0" id="settings-button">
|
||||
<svg fill="#8B949E" height="16"
|
||||
style="display: inline-block; user-select: none; vertical-align: text-bottom;" viewBox="0 0 16 16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 0a8.2 8.2 0 0 1 .701.031C9.444.095 9.99.645 10.16 1.29l.288 1.107c.018.066.079.158.212.224.231.114.454.243.668.386.123.082.233.09.299.071l1.103-.303c.644-.176 1.392.021 1.82.63.27.385.506.792.704 1.218.315.675.111 1.422-.364 1.891l-.814.806c-.049.048-.098.147-.088.294.016.257.016.515 0 .772-.01.147.038.246.088.294l.814.806c.475.469.679 1.216.364 1.891a7.977 7.977 0 0 1-.704 1.217c-.428.61-1.176.807-1.82.63l-1.102-.302c-.067-.019-.177-.011-.3.071a5.909 5.909 0 0 1-.668.386c-.133.066-.194.158-.211.224l-.29 1.106c-.168.646-.715 1.196-1.458 1.26a8.006 8.006 0 0 1-1.402 0c-.743-.064-1.289-.614-1.458-1.26l-.289-1.106c-.018-.066-.079-.158-.212-.224a5.738 5.738 0 0 1-.668-.386c-.123-.082-.233-.09-.299-.071l-1.103.303c-.644.176-1.392-.021-1.82-.63a8.12 8.12 0 0 1-.704-1.218c-.315-.675-.111-1.422.363-1.891l.815-.806c.05-.048.098-.147.088-.294a6.214 6.214 0 0 1 0-.772c.01-.147-.038-.246-.088-.294l-.815-.806C.635 6.045.431 5.298.746 4.623a7.92 7.92 0 0 1 .704-1.217c.428-.61 1.176-.807 1.82-.63l1.102.302c.067.019.177.011.3-.071.214-.143.437-.272.668-.386.133-.066.194-.158.211-.224l.29-1.106C6.009.645 6.556.095 7.299.03 7.53.01 7.764 0 8 0Zm-.571 1.525c-.036.003-.108.036-.137.146l-.289 1.105c-.147.561-.549.967-.998 1.189-.173.086-.34.183-.5.29-.417.278-.97.423-1.529.27l-1.103-.303c-.109-.03-.175.016-.195.045-.22.312-.412.644-.573.99-.014.031-.021.11.059.19l.815.806c.411.406.562.957.53 1.456a4.709 4.709 0 0 0 0 .582c.032.499-.119 1.05-.53 1.456l-.815.806c-.081.08-.073.159-.059.19.162.346.353.677.573.989.02.03.085.076.195.046l1.102-.303c.56-.153 1.113-.008 1.53.27.161.107.328.204.501.29.447.222.85.629.997 1.189l.289 1.105c.029.109.101.143.137.146a6.6 6.6 0 0 0 1.142 0c.036-.003.108-.036.137-.146l.289-1.105c.147-.561.549-.967.998-1.189.173-.086.34-.183.5-.29.417-.278.97-.423 1.529-.27l1.103.303c.109.029.175-.016.195-.045.22-.313.411-.644.573-.99.014-.031.021-.11-.059-.19l-.815-.806c-.411-.406-.562-.957-.53-1.456a4.709 4.709 0 0 0 0-.582c-.032-.499.119-1.05.53-1.456l.815-.806c.081-.08.073-.159.059-.19a6.464 6.464 0 0 0-.573-.989c-.02-.03-.085-.076-.195-.046l-1.102.303c-.56.153-1.113.008-1.53-.27a4.44 4.44 0 0 0-.501-.29c-.447-.222-.85-.629-.997-1.189l-.289-1.105c-.029-.11-.101-.143-.137-.146a6.6 6.6 0 0 0-1.142 0ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM9.5 8a1.5 1.5 0 1 0-3.001.001A1.5 1.5 0 0 0 9.5 8Z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div aria-label="Visit the GitHub issues website." class="btn btn-sm ml-3 tooltipped tooltipped-ne mb-1"
|
||||
id="smbtn1">
|
||||
<svg fill="#8B949E" height="16" style="display: inline-block; vertical-align: text-bottom;" viewBox="0 0 16 16"
|
||||
width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm9 3a1 1 0 11-2 0 1 1 0 012 0zM6.92 6.085c.081-.16.19-.299.34-.398.145-.097.371-.187.74-.187.28 0 .553.087.738.225A.613.613 0 019 6.25c0 .177-.04.264-.077.318a.956.956 0 01-.277.245c-.076.051-.158.1-.258.161l-.007.004a7.728 7.728 0 00-.313.195 2.416 2.416 0 00-.692.661.75.75 0 001.248.832.956.956 0 01.276-.245 6.3 6.3 0 01.26-.16l.006-.004c.093-.057.204-.123.313-.195.222-.149.487-.355.692-.662.214-.32.329-.702.329-1.15 0-.76-.36-1.348-.863-1.725A2.76 2.76 0 008 4c-.631 0-1.155.16-1.572.438-.413.276-.68.638-.849.977a.75.75 0 101.342.67z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Help
|
||||
</div>
|
||||
|
||||
<div aria-label="Visit the SteamDB instant search website." class="btn btn-sm ml-2 tooltipped tooltipped-n mb-1"
|
||||
id="smbtn2">
|
||||
<svg aria-hidden="true" class="octicon" fill="#8B949E" height="16" viewBox="0 0 128 128" width="16"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M63.9 0C30.5 0 3.1 11.9.1 27.1l35.6 6.7c2.9-.9 6.2-1.3 9.6-1.3l16.7-10c-.2-2.5 1.3-5.1 4.7-7.2 4.8-3.1 12.3-4.8 19.9-4.8 5.2-.1 10.5.7 15 2.2 11.2 3.8 13.7 11.1 5.7 16.3-5.1 3.3-13.3 5-21.4 4.8l-22 7.9c-.2 1.6-1.3 3.1-3.4 4.5-5.9 3.8-17.4 4.7-25.6 1.9-3.6-1.2-6-3-7-4.8L2.5 38.4c2.3 3.6 6 6.9 10.8 9.8C5 53 0 59 0 65.5c0 6.4 4.8 12.3 12.9 17.1C4.8 87.3 0 93.2 0 99.6 0 115.3 28.6 128 64 128c35.3 0 64-12.7 64-28.4 0-6.4-4.8-12.3-12.9-17 8.1-4.8 12.9-10.7 12.9-17.1 0-6.5-5-12.6-13.4-17.4 8.3-5.1 13.3-11.4 13.3-18.2 0-16.5-28.7-29.9-64-29.9zm22.8 14.2c-5.2.1-10.2 1.2-13.4 3.3-5.5 3.6-3.8 8.5 3.8 11.1 7.6 2.6 18.1 1.8 23.6-1.8s3.8-8.5-3.8-11c-3.1-1-6.7-1.5-10.2-1.5zm.3 1.7c7.4 0 13.3 2.8 13.3 6.2 0 3.4-5.9 6.2-13.3 6.2s-13.3-2.8-13.3-6.2c0-3.4 5.9-6.2 13.3-6.2zM45.3 34.4c-1.6.1-3.1.2-4.6.4l9.1 1.7a10.8 5 0 1 1-8.1 9.3l-8.9-1.7c1 .9 2.4 1.7 4.3 2.4 6.4 2.2 15.4 1.5 20-1.5s3.2-7.2-3.2-9.3c-2.6-.9-5.7-1.3-8.6-1.3zM109 51v9.3c0 11-20.2 19.9-45 19.9-24.9 0-45-8.9-45-19.9v-9.2c11.5 5.3 27.4 8.6 44.9 8.6 17.6 0 33.6-3.3 45.2-8.7zm0 34.6v8.8c0 11-20.2 19.9-45 19.9-24.9 0-45-8.9-45-19.9v-8.8c11.6 5.1 27.4 8.2 45 8.2s33.5-3.1 45-8.2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
SteamDB
|
||||
</div>
|
||||
|
||||
<div aria-label="Donate to the authors of the project." class="btn btn-sm ml-2 tooltipped tooltipped-nw mb-1"
|
||||
id="smbtn3">
|
||||
<svg fill="#8B949E" height="16" style="display: inline-block; vertical-align: text-bottom;" viewBox="0 0 16 16"
|
||||
width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 2.75A2.75 2.75 0 0 1 4.75 0c.983 0 1.873.42 2.57 1.232.268.318.497.668.68 1.042.183-.375.411-.725.68-1.044C9.376.42 10.266 0 11.25 0a2.75 2.75 0 0 1 2.45 4h.55c.966 0 1.75.784 1.75 1.75v2c0 .698-.409 1.301-1 1.582v4.918A1.75 1.75 0 0 1 13.25 16H2.75A1.75 1.75 0 0 1 1 14.25V9.332C.409 9.05 0 8.448 0 7.75v-2C0 4.784.784 4 1.75 4h.55c-.192-.375-.3-.8-.3-1.25ZM7.25 9.5H2.5v4.75c0 .138.112.25.25.25h4.5Zm1.5 0v5h4.5a.25.25 0 0 0 .25-.25V9.5Zm0-4V8h5.5a.25.25 0 0 0 .25-.25v-2a.25.25 0 0 0-.25-.25Zm-7 0a.25.25 0 0 0-.25.25v2c0 .138.112.25.25.25h5.5V5.5h-5.5Zm3-4a1.25 1.25 0 0 0 0 2.5h2.309c-.233-.818-.542-1.401-.878-1.793-.43-.502-.915-.707-1.431-.707ZM8.941 4h2.309a1.25 1.25 0 0 0 0-2.5c-.516 0-1 .205-1.43.707-.337.392-.646.975-.879 1.793Z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Donate
|
||||
</div>
|
||||
|
||||
<div aria-label="View the instructions on how to use SteamDepotDownloaderGUI."
|
||||
class="btn btn-sm ml-2 tooltipped tooltipped-nw mb-1"
|
||||
id="smbtn4">
|
||||
<svg fill="#8B949E" height="16" style="display: inline-block; vertical-align: text-bottom;" viewBox="0 0 16 16"
|
||||
width="16">
|
||||
<path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path>
|
||||
</svg>
|
||||
Instructions
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-surrounding" id="settings-surrounding">
|
||||
<div class="settings-content mx-auto" id="settings-content">
|
||||
<label class="version-info" id="version-info">UNKNOWN</label>
|
||||
<h3><b>Settings</b></h3>
|
||||
<hr>
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="form-group-header">
|
||||
<label>Theme</label>
|
||||
</div>
|
||||
<div class="form-group-body">
|
||||
<div class="BtnGroup">
|
||||
<button aria-selected="true" class="BtnGroup-item btn btn-sm" id="theme-auto" type="button">
|
||||
Auto
|
||||
</button>
|
||||
<button class="BtnGroup-item btn btn-sm" id="theme-light" type="button">
|
||||
Light
|
||||
</button>
|
||||
<button class="BtnGroup-item btn btn-sm" id="theme-dark" type="button">
|
||||
Dark
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<div class="form-group-header">
|
||||
<label for="terminal-dropdown">Override terminal selection (for Linux only)</label>
|
||||
</div>
|
||||
<div class="form-group-body">
|
||||
<div class="mb-2">
|
||||
<select class="form-select" id="terminal-dropdown">
|
||||
<option>Gnome Terminal</option>
|
||||
<option>KDE Konsole</option>
|
||||
<option>Xfce Terminal</option>
|
||||
<option>Terminator</option>
|
||||
<option>Terminology</option>
|
||||
<option>xterm</option>
|
||||
<option>Kitty</option>
|
||||
<option>LXTerminal</option>
|
||||
<option>Tilix</option>
|
||||
<option>Deepin Terminal</option>
|
||||
<option>cool-retro-term</option>
|
||||
<option selected="selected">Auto</option>
|
||||
</select>
|
||||
found: <span class="Counter"><code id="terminals-found">none</code></span>
|
||||
<br>default: <span class="Counter"><code id="default-terminal">none</code></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<div class="form-group-header">
|
||||
<label for="os-dropdown">Override OS selection</label>
|
||||
</div>
|
||||
<div class="form-group-body">
|
||||
<div class="mb-2">
|
||||
<select class="form-select" id="os-dropdown">
|
||||
<option>Windows</option>
|
||||
<option disabled>macOS (NOT YET IMPLEMENTED)</option>
|
||||
<option>Linux</option>
|
||||
<option>Print command in console (CTRL-SHIFT-I)</option>
|
||||
<option selected="selected">Auto</option>
|
||||
</select>
|
||||
default: <span class="Counter"><code id="default-os">none</code></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<div class="form-group-header">
|
||||
<label for="os-dropdown">Folder name</label>
|
||||
</div>
|
||||
<div class="form-group-body">
|
||||
<div class="BtnGroup">
|
||||
<button aria-selected="true" class="BtnGroup-item btn btn-sm" id="folder-name-appid"
|
||||
type="button">
|
||||
App-ID
|
||||
</button>
|
||||
<button class="BtnGroup-item btn btn-sm" id="folder-name-custom" type="button">
|
||||
Custom
|
||||
</button>
|
||||
</div>
|
||||
<br>
|
||||
<input class="form-control input-block" hidden id="folder-name-custom-input"
|
||||
placeholder="Game directory name"
|
||||
type="text"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
82
main.js
@@ -1,82 +0,0 @@
|
||||
const {app, BrowserWindow, dialog, ipcMain} = require("electron")
|
||||
const {platformpath} = require("./utils")
|
||||
require("v8-compile-cache")
|
||||
|
||||
let mainWindow
|
||||
const createWindow = () => {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({
|
||||
autoHideMenuBar: true,
|
||||
resizable: false,
|
||||
width: 445,
|
||||
height: 650,
|
||||
useContentSize: true,
|
||||
maximizable: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: true, contextIsolation: false
|
||||
}
|
||||
})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadFile("index.html")
|
||||
|
||||
// @formatter:off
|
||||
// Open the DevTools for debugging, only if not in production. This is removed for release by the build script (package.sh) because it's unreliable.
|
||||
// disable formatting so the line always stays the same so sed can find it
|
||||
// eslint-disable-next-line no-undef
|
||||
if (!app.isPackaged) mainWindow.webContents.openDevTools({mode: "detach"})
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.whenReady().then(() => {
|
||||
createWindow()
|
||||
|
||||
app.on("activate", () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
// If the page is fully loaded in, send a sign.
|
||||
app.on("web-contents-created", (event, contents) => {
|
||||
contents.on("dom-ready", () => {
|
||||
contents.send("ready")
|
||||
contents.send("version", app.getVersion()) // send the version to the renderer
|
||||
})
|
||||
})
|
||||
|
||||
// Quit when all windows are closed, except on macOS. There, it's common
|
||||
// for applications and their menu bar to stay active until the user quits
|
||||
// explicitly with Cmd + Q.
|
||||
app.on("window-all-closed", () => {
|
||||
if (process.platform !== "darwin") app.quit()
|
||||
})
|
||||
|
||||
|
||||
ipcMain.on("selectpath", (event) => {
|
||||
dialog.showOpenDialog(mainWindow, {
|
||||
// Specifying the Directory Selector Property
|
||||
properties: ["openDirectory"],
|
||||
title: "Select the path where the game will be downloaded",
|
||||
defaultPath: platformpath(),
|
||||
buttonLabel: "Select"
|
||||
}).then(file => {
|
||||
//console.debug("Has path selection succeeded: " + ((file.canceled) ? "NO" : "YES; see below")) --- doesn't log to the dev console TODO
|
||||
if (!file.canceled) {
|
||||
const filepath = file.filePaths[0].toString()
|
||||
// console.debug("Path selected is " + filepath) --- same thing
|
||||
event.reply("file", filepath)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
})
|
||||
|
||||
ipcMain.on("open-devtools", () => {
|
||||
mainWindow.webContents.openDevTools({mode: "detach"})
|
||||
})
|
||||
3809
package-lock.json
generated
61
package.json
@@ -1,41 +1,30 @@
|
||||
{
|
||||
"name": "steamdepotdownloadergui",
|
||||
"version": "2.4.1",
|
||||
"description": "DepotDownloader Electron frontend",
|
||||
"main": "main.js",
|
||||
"name": "vectum",
|
||||
"private": true,
|
||||
"version": "3.0.0",
|
||||
"type": "module",
|
||||
"license": "GPL-3.0-only",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "electron .",
|
||||
"build": "electron-builder -c electron-builder.yml -wl -p never",
|
||||
"buildall": "electron-builder -c electron-builder.yml -mwl -p never",
|
||||
"buildlinux": "electron-builder -c electron-builder.yml -l -p never",
|
||||
"buildwin": "electron-builder -c electron-builder.yml -w -p never",
|
||||
"buildmacos": "electron-builder -c electron-builder.yml -m -p never"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/mmvanheusden/SteamDepotDownloaderGUI.git"
|
||||
},
|
||||
"keywords": [
|
||||
"electron",
|
||||
"steam",
|
||||
"depotdownloader",
|
||||
"electron",
|
||||
"gui"
|
||||
],
|
||||
"author": "mmvanheusden",
|
||||
"license": "GPL-3.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/mmvanheusden/SteamDepotDownloaderGUI/issues"
|
||||
},
|
||||
"homepage": "https://github.com/mmvanheusden/SteamDepotDownloaderGUI#readme",
|
||||
"devDependencies": {
|
||||
"electron": "^28.2.2",
|
||||
"electron-builder": "^24.9.1",
|
||||
"eslint": "^8.56.0"
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"tauri": "tauri"
|
||||
},
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.5",
|
||||
"v8-compile-cache": "^2.4.0"
|
||||
}
|
||||
"@tauri-apps/api": "2.1.1",
|
||||
"@tauri-apps/plugin-dialog": "2.0.1",
|
||||
"@tauri-apps/plugin-shell": "2.0.1",
|
||||
"jquery": "^3.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.15.0",
|
||||
"@tauri-apps/cli": "2.1.0",
|
||||
"@types/eslint__js": "^8.42.3",
|
||||
"@types/jquery": "^3.5.32",
|
||||
"eslint": "^9.15.0",
|
||||
"typescript": "^5.6.3",
|
||||
"typescript-eslint": "^8.14.0",
|
||||
"vite": "^5.4.11"
|
||||
},
|
||||
"packageManager": "pnpm@9.13.2"
|
||||
}
|
||||
|
||||
25
package.sh
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
version=`jq '.version' package.json`
|
||||
version="${version//\"}"
|
||||
echo "version that will be build is ${version}"
|
||||
rm -rf ./dist/
|
||||
|
||||
# Disable DevTools for release, because it's unreliable
|
||||
original_line=$(sed -n '/if (!app.isPackaged) mainWindow.webContents.openDevTools({mode: "detach"})/p' main.js)
|
||||
# original_line has 1 tab too much, so we remove it
|
||||
original_line="${original_line// /}"
|
||||
sed -i 's/if (!app.isPackaged) mainWindow.webContents.openDevTools({mode: "detach"})/\/\/REMOVED FOR RELEASE./g' main.js
|
||||
|
||||
|
||||
npm run build
|
||||
mkdir -p ./dist/release-ready
|
||||
|
||||
cp ./dist/SteamDepotDownloaderGUI*.exe ./dist/release-ready/SteamDepotDownloaderGUI-windows-"${version}".exe
|
||||
cp `ls -d1 dist/* | grep -E "SteamDepotDownloaderGUI-[0-9]+\.[0-9]+\.[0-9]+\.AppImage"` ./dist/release-ready/SteamDepotDownloaderGUI-linux-"${version}"-x64.AppImage
|
||||
cp `ls -d1 dist/* | grep -E "steamdepotdownloadergui-[0-9]+\.[0-9]+\.[0-9]+\.zip"` ./dist/release-ready/SteamDepotDownloaderGUI-linux-"${version}"-x64.zip
|
||||
cp `ls -d1 dist/* | grep -E "SteamDepotDownloaderGUI-[0-9]+\.[0-9]+\.[0-9]+\-arm64.AppImage"` ./dist/release-ready/SteamDepotDownloaderGUI-linux-"${version}"-arm64.AppImage
|
||||
cp `ls -d1 dist/* | grep -E "steamdepotdownloadergui-[0-9]+\.[0-9]+\.[0-9]+\-arm64.zip"` ./dist/release-ready/SteamDepotDownloaderGUI-linux-"${version}"-arm64.zip
|
||||
echo "done building!"
|
||||
echo "reverting changes to main.js"
|
||||
sed -i "s|\/\/REMOVED FOR RELEASE.|$original_line|g" main.js
|
||||
1672
pnpm-lock.yaml
generated
Normal file
BIN
screenshot.png
|
Before Width: | Height: | Size: 75 KiB |
16
src-tauri/.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
||||
|
||||
# Generated by Tauri
|
||||
# will have schema files for capabilities auto-completion
|
||||
/gen/schemas
|
||||
|
||||
|
||||
# DepotDownloader
|
||||
depot/
|
||||
downloads/
|
||||
.DepotDownloader/
|
||||
Games/
|
||||
Depots/
|
||||
**/*.sh
|
||||
5861
src-tauri/Cargo.lock
generated
Normal file
28
src-tauri/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "vectum"
|
||||
version = "3.0.0"
|
||||
description = "Download older versions of Steam games with DepotDownloader"
|
||||
authors = ["mmvanheusden"]
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.0.3", features = [] }
|
||||
|
||||
[dependencies]
|
||||
fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs" }
|
||||
tauri = { version = "2.1.1", features = [] }
|
||||
tauri-plugin-shell = "2.0.2"
|
||||
tauri-plugin-dialog = "2.0.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
derive-getters = "0.5.0"
|
||||
sha256 = "1.5.0"
|
||||
reqwest = { version = "0.12.9",features = ["blocking"] }
|
||||
zip = "2.2.0"
|
||||
async-process = "2.3.0"
|
||||
|
||||
[profile.dev]
|
||||
incremental = true
|
||||
3
src-tauri/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
||||
12
src-tauri/capabilities/default.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Capability for the main window",
|
||||
"windows": ["main"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"shell:allow-open",
|
||||
"dialog:default",
|
||||
"shell:default"
|
||||
]
|
||||
}
|
||||
BIN
src-tauri/icons/128x128.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src-tauri/icons/128x128@2x.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
src-tauri/icons/32x32.png
Normal file
|
After Width: | Height: | Size: 974 B |
BIN
src-tauri/icons/Square107x107Logo.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
src-tauri/icons/Square142x142Logo.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
src-tauri/icons/Square150x150Logo.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
src-tauri/icons/Square284x284Logo.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
src-tauri/icons/Square30x30Logo.png
Normal file
|
After Width: | Height: | Size: 903 B |
BIN
src-tauri/icons/Square310x310Logo.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
src-tauri/icons/Square44x44Logo.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src-tauri/icons/Square71x71Logo.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src-tauri/icons/Square89x89Logo.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
src-tauri/icons/StoreLogo.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src-tauri/icons/icon.icns
Normal file
BIN
src-tauri/icons/icon.ico
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
src-tauri/icons/icon.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
112
src-tauri/src/depotdownloader.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use std::fs::File;
|
||||
use std::io::ErrorKind::AlreadyExists;
|
||||
use std::{fs, io};
|
||||
use std::{io::Write, path::Path};
|
||||
|
||||
use crate::get_os;
|
||||
use reqwest;
|
||||
use sha256;
|
||||
|
||||
pub static DEPOTDOWNLOADER_VERSION: &str = "2.7.4";
|
||||
|
||||
pub fn calc_checksum(path: &Path) -> io::Result<String> {
|
||||
let bytes = fs::read(path)?;
|
||||
let hash = sha256::digest(&bytes);
|
||||
Ok(hash)
|
||||
}
|
||||
|
||||
/**
|
||||
See: [`test_get_depotdownloader_url()`]
|
||||
*/
|
||||
pub fn get_depotdownloader_url() -> String {
|
||||
let arch = match std::env::consts::ARCH {
|
||||
"x86_64" => "x64",
|
||||
"aarch64" => "arm64",
|
||||
"arm" => "arm",
|
||||
_ => "x86_64",
|
||||
};
|
||||
|
||||
format!("https://github.com/SteamRE/DepotDownloader/releases/download/DepotDownloader_{}/DepotDownloader-{}-{}.zip", DEPOTDOWNLOADER_VERSION, get_os(), arch)
|
||||
}
|
||||
|
||||
/// Downloads a file. The file will be saved to the [`filename`] provided.
|
||||
pub async fn download_file(url: &str, filename: &Path) -> io::Result<()> {
|
||||
if filename.exists() {
|
||||
println!("DEBUG: Not downloading. File already exists.");
|
||||
return Err(io::Error::from(AlreadyExists));
|
||||
}
|
||||
|
||||
let mut file = File::create(filename)?;
|
||||
let response = reqwest::get(url)
|
||||
.await
|
||||
.expect("Failed to contact internet.");
|
||||
|
||||
let content = response.bytes().await.unwrap();
|
||||
|
||||
file.write_all(&content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unzips DepotDownloader zips
|
||||
pub fn unzip(zip_file: &Path) -> io::Result<()> {
|
||||
let file = File::open(zip_file)?;
|
||||
let mut archive = zip::ZipArchive::new(file)?;
|
||||
|
||||
for i in 0..archive.len() {
|
||||
let mut file = archive.by_index(i)?;
|
||||
|
||||
let outpath = match file.enclosed_name() {
|
||||
Some(path) => path,
|
||||
None => continue
|
||||
};
|
||||
|
||||
println!("Extracted {} from archive.", outpath.display());
|
||||
|
||||
if let Some(p) = outpath.parent() {
|
||||
if !p.exists() {
|
||||
fs::create_dir_all(p)?;
|
||||
}
|
||||
}
|
||||
let mut outfile = File::create(&outpath)?;
|
||||
io::copy(&mut file, &mut outfile)?;
|
||||
|
||||
|
||||
// Copy over permissions from enclosed file to extracted file on Unix and macOS systems.
|
||||
if get_os() != "windows" {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
// If the mode `file.unix_mode()` is something (not None), copy it over to the extracted file.
|
||||
if let Some(mode) = file.unix_mode() {
|
||||
fs::set_permissions(&outpath, fs::Permissions::from_mode(mode))?;
|
||||
}
|
||||
|
||||
// Set DepotDownloader executable.
|
||||
if outpath.display().to_string() == "DepotDownloader" {
|
||||
fs::set_permissions(&outpath, fs::Permissions::from_mode(0o755))?; // WTF is an octal?
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use reqwest::blocking;
|
||||
|
||||
#[test]
|
||||
/// checks if all possible DepotDownloader URLs exist.
|
||||
fn test_get_depotdownloader_url() {
|
||||
for os in ["windows", "linux", "macos"].iter() {
|
||||
for arch in ["x64", "arm64", "arm"].iter() {
|
||||
if arch.eq(&"arm") && !os.eq(&"linux") {
|
||||
continue;
|
||||
}
|
||||
let url = format!("https://github.com/SteamRE/DepotDownloader/releases/download/DepotDownloader_{}/DepotDownloader-{}-{}.zip", DEPOTDOWNLOADER_VERSION, os, arch);
|
||||
println!("Testing DepotDownloader URL: {}", url);
|
||||
|
||||
assert!(blocking::get(url).unwrap().status().is_success());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
149
src-tauri/src/main.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
use std::sync::OnceLock;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::depotdownloader::{get_depotdownloader_url, DEPOTDOWNLOADER_VERSION};
|
||||
use crate::terminal::Terminal;
|
||||
use tauri::{AppHandle, Emitter};
|
||||
|
||||
mod depotdownloader;
|
||||
mod steam;
|
||||
mod terminal;
|
||||
|
||||
/// The first terminal found. Used as default terminal.
|
||||
static TERMINAL: OnceLock<Vec<Terminal>> = OnceLock::new(); // We create this variable now, and quickly populate it in preload_vectum(). we then later access the data in start_download()
|
||||
|
||||
/// This function is called every time the app is reloaded/started. It quickly populates the [`TERMINAL`] variable with a working terminal.
|
||||
#[tauri::command]
|
||||
async fn preload_vectum(app: AppHandle) {
|
||||
// Only fill this variable once.
|
||||
if TERMINAL.get().is_none() {
|
||||
TERMINAL
|
||||
.set(terminal::get_installed_terminals(true).await)
|
||||
.expect("Failed to set available terminals")
|
||||
}
|
||||
|
||||
// Send the default terminal name to the frontend.
|
||||
app.emit(
|
||||
"default-terminal",
|
||||
Terminal::pretty_name(&TERMINAL.get().unwrap()[0]),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn start_download(steam_download: steam::SteamDownload) {
|
||||
let default_terminal = TERMINAL.get().unwrap();
|
||||
let working_dir = env::current_dir().unwrap();
|
||||
|
||||
let terminal_to_use = if steam_download.options().terminal().is_none() { default_terminal.first().unwrap() } else { &Terminal::from_index(&steam_download.options().terminal().unwrap()).unwrap() };
|
||||
|
||||
println!("\n-------------------------DEBUG INFO------------------------");
|
||||
println!("received these values from frontend:");
|
||||
println!("\t- Username: {}", steam_download.username().as_ref().unwrap_or(&String::from("Not provided")));
|
||||
// println!("\t- Password: {}", steam_download.password().as_ref().unwrap_or(&String::from("Not provided"))); Don't log in prod lol
|
||||
println!("\t- App ID: {}", steam_download.app_id());
|
||||
println!("\t- Depot ID: {}", steam_download.depot_id());
|
||||
println!("\t- Manifest ID: {}", steam_download.manifest_id());
|
||||
println!("\t- Output Path: {}", steam_download.output_path());
|
||||
println!("\t- Default terminal: {}", Terminal::pretty_name(&default_terminal[0]));
|
||||
println!("\t- Terminal command: {:?}", terminal_to_use.create_command(&steam_download));
|
||||
println!("\t- Working directory: {}", working_dir.display());
|
||||
println!("----------------------------------------------------------\n");
|
||||
|
||||
terminal_to_use.create_command(&steam_download).spawn().ok();
|
||||
}
|
||||
|
||||
/// Downloads the DepotDownloader zip file from the internet based on the OS.
|
||||
#[tauri::command]
|
||||
async fn download_depotdownloader() {
|
||||
let url = get_depotdownloader_url();
|
||||
|
||||
// Where we store the DepotDownloader zip.
|
||||
let zip_filename = format!("DepotDownloader-v{}-{}.zip", DEPOTDOWNLOADER_VERSION, env::consts::OS);
|
||||
let depotdownloader_zip = Path::new(&zip_filename);
|
||||
|
||||
println!("Downloading DepotDownloader for {} to {}/{}", env::consts::OS, env::current_dir().unwrap().display(), depotdownloader_zip.display());
|
||||
|
||||
match depotdownloader::download_file(url.as_str(), depotdownloader_zip).await {
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::AlreadyExists {
|
||||
println!("DepotDownloader already exists. Skipping download.");
|
||||
return;
|
||||
}
|
||||
|
||||
println!("Failed to download DepotDownloader: {}", e);
|
||||
return;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
println!("Succesfully downloaded DepotDownloader from {}", url);
|
||||
|
||||
depotdownloader::unzip(depotdownloader_zip).unwrap();
|
||||
println!("Succesfully extracted DepotDownloader zip.");
|
||||
}
|
||||
|
||||
/// Checks internet connectivity using Google
|
||||
#[tauri::command]
|
||||
async fn internet_connection() -> bool {
|
||||
let client = reqwest::Client::builder().timeout(Duration::from_secs(5)).build().unwrap();
|
||||
|
||||
client.get("https://connectivitycheck.android.com/generate_204").send().await.is_ok()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn get_all_terminals(app: AppHandle) {
|
||||
let terminals = terminal::get_installed_terminals(false).await;
|
||||
|
||||
terminals.iter().for_each(|terminal| {
|
||||
println!("Terminal #{} ({}) is installed!", terminal.index().unwrap(), terminal.pretty_name());
|
||||
|
||||
// Sends: (terminal index aligned with dropdown; total terminals)
|
||||
app.emit("working-terminal", (terminal.index(), Terminal::total())).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_os() -> &'static str {
|
||||
match env::consts::OS {
|
||||
"linux" => "linux",
|
||||
"macos" => "macos",
|
||||
"windows" => "windows",
|
||||
_ => "unknown",
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// macOS: change dir to documents because upon opening, our current dir by default is "/".
|
||||
if get_os() == "macos" {
|
||||
let _ = fix_path_env::fix(); // todo: does this actually do something useful
|
||||
let documents_dir = format!(
|
||||
"{}/Documents/SteamDepotDownloaderGUI",
|
||||
std::env::var_os("HOME").unwrap().to_str().unwrap()
|
||||
);
|
||||
let documents_dir = Path::new(&documents_dir);
|
||||
// println!("{}", documents_dir.display());
|
||||
|
||||
std::fs::create_dir_all(documents_dir).unwrap();
|
||||
env::set_current_dir(documents_dir).unwrap();
|
||||
}
|
||||
|
||||
println!();
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
start_download,
|
||||
download_depotdownloader,
|
||||
internet_connection,
|
||||
preload_vectum,
|
||||
get_all_terminals
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
41
src-tauri/src/steam.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use std::path::PathBuf;
|
||||
use derive_getters::Getters;
|
||||
use serde::Deserialize;
|
||||
|
||||
|
||||
/// Represents the data required to download a Steam depot.
|
||||
#[derive(Deserialize, Debug, Getters)]
|
||||
pub struct SteamDownload {
|
||||
username: Option<String>,
|
||||
password: Option<String>,
|
||||
app_id: String,
|
||||
depot_id: String,
|
||||
manifest_id: String,
|
||||
options: VectumOptions
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Getters)]
|
||||
pub struct VectumOptions {
|
||||
terminal: Option<u8>,
|
||||
output_directory: Option<PathBuf>,
|
||||
directory_name: Option<String>
|
||||
}
|
||||
|
||||
|
||||
impl SteamDownload {
|
||||
/// If a username or password are not provided, the download is considered anonymous
|
||||
pub fn is_anonymous(&self) -> bool {
|
||||
self.username.is_none() || self.password.is_none()
|
||||
}
|
||||
|
||||
/// The directory where the download should happen
|
||||
pub fn output_path(&self) -> String {
|
||||
let sep = std::path::MAIN_SEPARATOR.to_string();
|
||||
match (&self.options.output_directory, &self.options.directory_name) {
|
||||
(Some(output_dir), Some(dir_name)) => format!("{}{}{}", output_dir.display(), sep, dir_name),
|
||||
(Some(output_dir), None) => format!("{}{}{}", output_dir.display(), sep, &self.manifest_id),
|
||||
(None, Some(dir_name)) => format!(".{}{}", sep, dir_name),
|
||||
(None, None) => format!(".{}{}", sep, &self.manifest_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
414
src-tauri/src/terminal.rs
Normal file
@@ -0,0 +1,414 @@
|
||||
use crate::steam::SteamDownload;
|
||||
use async_process::Command;
|
||||
use serde::Serialize;
|
||||
use std::{env, fs};
|
||||
use std::fs::File;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use crate::get_os;
|
||||
|
||||
/// Represents a terminal that can be used to run commands.
|
||||
/// **Should be in sync with the terminal dropdown in the frontend.**
|
||||
#[derive(Debug, Serialize, PartialEq)]
|
||||
pub enum Terminal {
|
||||
GNOMETerminal,
|
||||
Alacritty,
|
||||
Konsole,
|
||||
GNOMEConsole,
|
||||
Xfce4Terminal,
|
||||
DeepinTerminal,
|
||||
Terminator,
|
||||
Terminology,
|
||||
Kitty,
|
||||
LXTerminal,
|
||||
Tilix,
|
||||
CoolRetroTerm,
|
||||
XTerm,
|
||||
CMD,
|
||||
Terminal
|
||||
}
|
||||
|
||||
|
||||
impl Terminal {
|
||||
/// Iterates through each terminal
|
||||
pub fn iter() -> impl Iterator<Item=Terminal> {
|
||||
use self::Terminal::*;
|
||||
|
||||
vec![
|
||||
GNOMETerminal, Alacritty, Konsole, GNOMEConsole, Xfce4Terminal, DeepinTerminal, Terminator, Terminology, Kitty, LXTerminal, Tilix, CoolRetroTerm, XTerm, CMD, Terminal
|
||||
].into_iter()
|
||||
}
|
||||
|
||||
/// Get terminal from index in order of the [`Terminal`] enum
|
||||
pub fn from_index(index: &u8) -> Option<Terminal> {
|
||||
Terminal::iter().nth(*index as usize)
|
||||
}
|
||||
|
||||
/// Get the index of a terminal in the order of the [`Terminal`] enum
|
||||
/// Returns `None` if the terminal is not found.
|
||||
pub fn index(&self) -> Option<u8> {
|
||||
Terminal::iter().position(|x| x == *self).map(|x| x as u8)
|
||||
}
|
||||
|
||||
|
||||
/// Get total number of terminals **possible** depending on the OS
|
||||
pub fn total() -> u8 {
|
||||
if get_os() != "windows" || get_os() == "macos" {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Terminal::iter().count() as u8 - 1 // -1 because cmd is not available on linux
|
||||
}
|
||||
|
||||
/// Get the pretty name of a terminal
|
||||
pub fn pretty_name(&self) -> &str {
|
||||
match self {
|
||||
Terminal::GNOMETerminal => "GNOME Terminal",
|
||||
Terminal::GNOMEConsole => "GNOME Console",
|
||||
Terminal::Konsole => "Konsole",
|
||||
Terminal::Xfce4Terminal => "Xfce Terminal",
|
||||
Terminal::Terminator => "Terminator",
|
||||
Terminal::Terminology => "Terminology",
|
||||
Terminal::XTerm => "XTerm",
|
||||
Terminal::Kitty => "Kitty",
|
||||
Terminal::LXTerminal => "LXTerminal",
|
||||
Terminal::Tilix => "Tilix",
|
||||
Terminal::DeepinTerminal => "Deepin Terminal",
|
||||
Terminal::CoolRetroTerm => "cool-retro-term",
|
||||
Terminal::Alacritty => "Alacritty",
|
||||
Terminal::CMD => "cmd",
|
||||
Terminal::Terminal => "Terminal"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//region Probing a terminal
|
||||
/// Checks if a [`Terminal`] is installed.
|
||||
/// **See:** [`get_installed_terminals`]
|
||||
pub async fn installed(&self) -> bool {
|
||||
match self {
|
||||
Terminal::CMD => { get_os() == "windows" }
|
||||
Terminal::GNOMETerminal => {
|
||||
let mut cmd = Command::new("gnome-terminal");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::GNOMEConsole => {
|
||||
let mut cmd = Command::new("kgx");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::Konsole => {
|
||||
let mut cmd = Command::new("konsole");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::Xfce4Terminal => {
|
||||
let mut cmd = Command::new("xfce4-terminal");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::Terminator => {
|
||||
let mut cmd = Command::new("terminator");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::Terminology => {
|
||||
let mut cmd = Command::new("terminology");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::XTerm => {
|
||||
let mut cmd = Command::new("xterm");
|
||||
cmd.arg("-v").output().await.is_ok()
|
||||
}
|
||||
Terminal::Kitty => {
|
||||
let mut cmd = Command::new("kitty");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::LXTerminal => {
|
||||
let mut cmd = Command::new("lxterminal");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::Tilix => {
|
||||
let mut cmd = Command::new("tilix");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::DeepinTerminal => {
|
||||
let mut cmd = Command::new("deepin-terminal");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::CoolRetroTerm => {
|
||||
let mut cmd = Command::new("cool-retro-term");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::Alacritty => {
|
||||
let mut cmd = Command::new("alacritty");
|
||||
cmd.arg("--version").output().await.is_ok()
|
||||
}
|
||||
Terminal::Terminal => { get_os() == "macos" }
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
|
||||
//region Running a command in the terminal
|
||||
/**
|
||||
Returns a [`Command`] that, when executed should open the terminal and run the command.
|
||||
|
||||
|
||||
## Commands
|
||||
`{command}` = `{command};echo Command finished with code $?;sleep infinity`
|
||||
|
||||
| Terminal | Command to open terminal |
|
||||
|------------------|--------------------------------------------------------------------------|
|
||||
| cmd | `start cmd.exe /k {command}` |
|
||||
| GNOMETerminal | `gnome-terminal -- /usr/bin/env sh -c {command}` |
|
||||
| GNOMEConsole | `kgx -e /usr/bin/env sh -c {command}` |
|
||||
| Konsole | `konsole -e /usr/bin/env sh -c {command}` |
|
||||
| Xfce4Terminal | `xfce4-terminal -x /usr/bin/env sh -c {command}` |
|
||||
| Terminator | `terminator -T "Downloading depot..." -e {command}` |
|
||||
| Terminology | `terminology -e /usr/bin/env sh -c {command}` |
|
||||
| XTerm | `xterm -hold -T "Downloading depot..." -e /usr/bin/env sh -c {command}` |
|
||||
| Kitty | `kitty /usr/bin/env sh -c {command}` |
|
||||
| LXTerminal | `lxterminal -e /usr/bin/env sh -c {command}` |
|
||||
| Tilix | `tilix -e /usr/bin/env sh -c {command}` |
|
||||
| DeepinTerminal | `deepin-terminal -e /usr/bin/env sh -c {command}` |
|
||||
| CoolRetroTerm | `cool-retro-term -e /usr/bin/env sh -c {command}` |
|
||||
| Alacritty | `alacritty -e /usr/bin/env sh -c {command}` |
|
||||
| Terminal (macOS) | We create a bash script and run that using `open`. |
|
||||
|
||||
*/
|
||||
pub fn create_command(&self, steam_download: &SteamDownload) -> Command {
|
||||
let command = create_depotdownloader_command(steam_download);
|
||||
|
||||
match self {
|
||||
Terminal::CMD => {
|
||||
let mut cmd = Command::new("cmd.exe");
|
||||
cmd.args(&["/c", "start", "PowerShell.exe", "-NoExit", "-Command"]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::GNOMETerminal => {
|
||||
let mut cmd = Command::new("gnome-terminal");
|
||||
cmd.args([
|
||||
"--",
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::GNOMEConsole => {
|
||||
let mut cmd = Command::new("kgx");
|
||||
cmd.args([
|
||||
"-e",
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c"
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::Konsole => {
|
||||
let mut cmd = Command::new("konsole");
|
||||
cmd.args([
|
||||
"-e",
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::Xfce4Terminal => {
|
||||
let mut cmd = Command::new("xfce4-terminal");
|
||||
cmd.args([
|
||||
"-x",
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::Terminator => {
|
||||
let mut cmd = Command::new("terminator");
|
||||
cmd.args([
|
||||
"-T",
|
||||
"Downloading depot...",
|
||||
"-e",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::Terminology => {
|
||||
let mut cmd = Command::new("terminology");
|
||||
cmd.args([
|
||||
"-e",
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::XTerm => {
|
||||
let mut cmd = Command::new("xterm");
|
||||
cmd.args([
|
||||
"-hold",
|
||||
"-T",
|
||||
"Downloading depot...",
|
||||
"-e",
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::Kitty => {
|
||||
let mut cmd = Command::new("kitty");
|
||||
cmd.args([
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::LXTerminal => {
|
||||
let mut cmd = Command::new("lxterminal");
|
||||
cmd.args([
|
||||
"-e",
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::Tilix => {
|
||||
let mut cmd = Command::new("tilix");
|
||||
cmd.args([
|
||||
"-e",
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::DeepinTerminal => {
|
||||
let mut cmd = Command::new("deepin-terminal");
|
||||
cmd.args([
|
||||
"-e",
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::CoolRetroTerm => {
|
||||
let mut cmd = Command::new("cool-retro-term");
|
||||
cmd.args([
|
||||
"-e",
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::Alacritty => {
|
||||
let mut cmd = Command::new("alacritty");
|
||||
cmd.args([
|
||||
"-e",
|
||||
"/usr/bin/env",
|
||||
"sh",
|
||||
"-c",
|
||||
]).args(command);
|
||||
cmd
|
||||
}
|
||||
Terminal::Terminal => {
|
||||
// Create a bash script and run that. Not very secure but it makes this easier.
|
||||
let download_script = format!("#!/bin/bash\ncd {}\n{}",env::current_dir().unwrap().display(), command[0]);
|
||||
// println!("{}", download_script);
|
||||
|
||||
fs::write("./script.sh", download_script).unwrap();
|
||||
fs::set_permissions("./script.sh", fs::Permissions::from_mode(0o755)).unwrap(); // Won't run without executable permission
|
||||
|
||||
let mut cmd = Command::new("/usr/bin/open");
|
||||
cmd.args(&["-a", "Terminal", "./script.sh"]);
|
||||
cmd
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
}
|
||||
|
||||
/**
|
||||
Checks if terminals are installed by checking if they respond to commands.
|
||||
|
||||
## How it works
|
||||
Probes a list of popular terminals and checks if they return an error when calling their `--version` or similar command line flag.
|
||||
|
||||
## Options
|
||||
* `return_immediately`: [`bool`]: Return as soon as one terminal is found.
|
||||
|
||||
## Returns
|
||||
A vector containing a list of terminals that should work.
|
||||
|
||||
## Commands
|
||||
| Terminal | Command to check if installed |
|
||||
|----------------|-------------------------------|
|
||||
| cmd | `cmd /?` |
|
||||
| GNOMETerminal | `gnome-terminal --version` |
|
||||
| GNOMEConsole | `kgx --version` |
|
||||
| Konsole | `konsole --version` |
|
||||
| Xfce4Terminal | `xfce4-terminal --version` |
|
||||
| Terminator | `terminator --version` |
|
||||
| Terminology | `terminology --version` |
|
||||
| XTerm | `xterm -v` |
|
||||
| Kitty | `kitty --version` |
|
||||
| LXTerminal | `lxterminal --version` |
|
||||
| Tilix | `tilix --version` |
|
||||
| DeepinTerminal | `deepin-terminal --version` |
|
||||
| CoolRetroTerm | `cool-retro-term --version` |
|
||||
| Alacritty | `alacritty --version` |
|
||||
|
||||
*/
|
||||
pub async fn get_installed_terminals(return_immediately: bool) -> Vec<Terminal> {
|
||||
match get_os() {
|
||||
"windows" => { return vec!(Terminal::CMD); }
|
||||
"macos" => { return vec!(Terminal::Terminal); }
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
||||
let mut available_terminals: Vec<Terminal> = Vec::new();
|
||||
|
||||
for terminal in Terminal::iter() {
|
||||
// Probe terminal. If it doesn't raise an error, it is probably installed.
|
||||
if terminal.installed().await {
|
||||
if return_immediately {
|
||||
return vec![terminal];
|
||||
}
|
||||
available_terminals.push(terminal);
|
||||
}
|
||||
}
|
||||
|
||||
if available_terminals.is_empty() {
|
||||
eprintln!("No terminals were detected. Try installing one.");
|
||||
}
|
||||
|
||||
available_terminals
|
||||
}
|
||||
|
||||
/// Creates the DepotDownloader command necessary to download the requested manifest.
|
||||
fn create_depotdownloader_command(steam_download: &SteamDownload) -> Vec<String> {
|
||||
let output_dir = if get_os() == "windows" {
|
||||
// In PowerShell, spaces can be escaped with a backtick.
|
||||
steam_download.output_path().replace(" ", "` ")
|
||||
} else {
|
||||
// In bash, spaces can be escaped with a backslash.
|
||||
steam_download.output_path().replace(" ", "\\ ")
|
||||
};
|
||||
|
||||
|
||||
if cfg!(not(windows)) {
|
||||
if steam_download.is_anonymous() {
|
||||
vec![format!(r#"./DepotDownloader -app {} -depot {} -manifest {} -dir {};echo Done!;sleep infinity"#, steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
|
||||
} else {
|
||||
vec![format!(r#"./DepotDownloader -username {} -password {} -app {} -depot {} -manifest {} -dir {};echo Done!;sleep infinity"#, steam_download.username().clone().unwrap(), steam_download.password().clone().unwrap(), steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
|
||||
}
|
||||
} else {
|
||||
if steam_download.is_anonymous() {
|
||||
vec![format!(r#".\DepotDownloader.exe -app {} -depot {} -manifest {} -dir {}"#, steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
|
||||
} else {
|
||||
vec![format!(r#".\DepotDownloader.exe -username {} -password {} -app {} -depot {} -manifest {} -dir {}"#, steam_download.username().clone().unwrap(), steam_download.password().clone().unwrap(), steam_download.app_id(), steam_download.depot_id(), steam_download.manifest_id(), output_dir)]
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src-tauri/tauri.conf.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"productName": "SteamDepotDownloaderGUI",
|
||||
"version": "3.0.0",
|
||||
"identifier": "net.oopium.depotdownloader",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
"devUrl": "http://localhost:1420",
|
||||
"beforeBuildCommand": "pnpm build",
|
||||
"frontendDist": "../dist"
|
||||
},
|
||||
"app": {
|
||||
"withGlobalTauri": true,
|
||||
"windows": [
|
||||
{
|
||||
"title": "SteamDepotDownloaderGUI",
|
||||
"width": 445,
|
||||
"height": 650,
|
||||
"resizable": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
src/assets/Windows.woff
Normal file
135
src/css/style.css
Normal file
@@ -0,0 +1,135 @@
|
||||
@font-face {
|
||||
font-family: 'Hubot Sans';
|
||||
src: url('../assets/Hubot-Sans.woff2') format('woff2 supports variations'),
|
||||
url('../assets/Hubot-Sans.woff2') format('woff2-variations');
|
||||
font-weight: 700;
|
||||
font-stretch: expanded;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Windows';
|
||||
src: url('../assets/Windows.woff') format('woff2 supports variations'),
|
||||
url('../assets/Windows.woff') format('woff2-variations');
|
||||
font-weight: 700;
|
||||
font-stretch: expanded;
|
||||
}
|
||||
|
||||
.f1-light {
|
||||
font-family: 'Hubot Sans', sans-serif;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* The grey part */
|
||||
.settings-surrounding {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: rgba(0, 0, 0, 0.33);
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
overflow: auto;
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
background-color: var(--bgColor-default, var(--color-canvas-default));
|
||||
margin: 5%;
|
||||
padding: 25px;
|
||||
border: 1.5px solid white;
|
||||
width: 90vw; /* 90vw -> 90% */
|
||||
height: 90vh; /* 90vh -> 90% */
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), 0 6px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
[data-color-mode="light"] .settings-content {
|
||||
border: 1.5px solid black;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
[data-color-mode="auto"] .settings-content {
|
||||
border: 1.5px solid black;
|
||||
}
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
background: black linear-gradient(to right, #0c1016, #ccc, #0c1016);
|
||||
}
|
||||
|
||||
[data-color-mode="light"] hr {
|
||||
filter: invert(1);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
[data-color-mode="auto"] hr {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
|
||||
.version-info {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
font-size: 0.9em;
|
||||
padding: 5px 10px;
|
||||
font-family: monospace;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.AnimatedEllipsis {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
vertical-align: bottom
|
||||
}
|
||||
|
||||
.AnimatedEllipsis::after {
|
||||
display: inline-block;
|
||||
content: "...";
|
||||
animation: AnimatedEllipsis-keyframes 1s steps(4, jump-none) infinite
|
||||
}
|
||||
|
||||
@keyframes AnimatedEllipsis-keyframes {
|
||||
0% {
|
||||
transform: translateX(-100%)
|
||||
}
|
||||
}
|
||||
|
||||
.opium-button {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
cursor: pointer;
|
||||
margin-left: 5px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
border: 1px solid #000;
|
||||
background: linear-gradient(180deg, #8C8C8C 25%, #434343 75%);
|
||||
display: inline-block;
|
||||
font: 16px "Windows", monospace;
|
||||
padding: 2px 5px;
|
||||
color: darkred;
|
||||
text-decoration: none;
|
||||
|
||||
}
|
||||
|
||||
.opium-button:hover {
|
||||
cursor: zoom-in;
|
||||
background: linear-gradient(180deg, #b0b0b0 25%, #504f4f 75%);
|
||||
}
|
||||
|
||||
.opium-button:active {
|
||||
cursor: crosshair;
|
||||
border: 1px inset black;
|
||||
background: linear-gradient(180deg, #333232 25%, #504f4f 75%);
|
||||
}
|
||||
276
src/index.html
Normal file
@@ -0,0 +1,276 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-color-mode="auto" data-dark-theme="dark" data-light-theme="light" id="theme" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||
<title>SteamDepotDownloaderGUI</title>
|
||||
<link href="https://unpkg.com/@primer/css@21.3.6/dist/primer.css" rel="stylesheet"/>
|
||||
<link href="css/style.css" rel="stylesheet">
|
||||
<script defer src="./ts/preload.ts" type="module"></script>
|
||||
<script src="./ts/main.ts" type="module"></script>
|
||||
<script src="./ts/settings.ts" type="module"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="mx-auto">
|
||||
<div>
|
||||
<div class="f1-light text-center">Steam Depot Downloader</div>
|
||||
<form id="theform">
|
||||
<div class="form-group mx-3 mt-1">
|
||||
<div class="form-group-header">
|
||||
<label for="username">Username</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="username" placeholder="Leave empty for anonymous download"
|
||||
type="text"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1">
|
||||
<div class="form-group-header">
|
||||
<label for="password">Password</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="password" placeholder="Leave empty for anonymous download"
|
||||
type="password"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label for="appid">App ID</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="appid" type="number"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label for="depotid">Depot ID</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="depotid" type="number"/>
|
||||
</div>
|
||||
|
||||
<div class="form-group mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label for="manifestid">Manifest ID</label>
|
||||
</div>
|
||||
<input class="form-control input-block" id="manifestid" type="number"/>
|
||||
</div>
|
||||
|
||||
<div class="mx-3 mt-1 required">
|
||||
<div class="form-group-header">
|
||||
<label>Download Location</label>
|
||||
</div>
|
||||
<div aria-label="Pick the path/location where the game will be downloaded to."
|
||||
class="form-control btn btn-sm tooltipped tooltipped-ne" id="pickpath">
|
||||
Set location
|
||||
</div>
|
||||
|
||||
<div aria-disabled="true" aria-label="Check the location that has been selected."
|
||||
class="form-control btn btn-sm ml-2 tooltipped tooltipped-ne" id="checkpath">
|
||||
Open location
|
||||
</div>
|
||||
|
||||
<span class="Label mt-1 ml-3 Label--warning" id="busy">
|
||||
<span aria-label="Application is executing a task. Please be patient."
|
||||
class="tooltipped tooltipped-n">Busy<span class="AnimatedEllipsis"></span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="internet-btns">
|
||||
<div class="form-group mt-3 ml-3 mr-3">
|
||||
<div class="BtnGroup d-flex">
|
||||
<button class="BtnGroup-item btn btn-block btn-primary flex-1" id="downloadbtn">
|
||||
<svg class="octicon filter-red" height="16"
|
||||
style="display: inline-block; user-select: none; vertical-align: text-bottom;"
|
||||
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5 0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Download
|
||||
</button>
|
||||
<button aria-disabled="true" class="BtnGroup-item btn flex-0" id="settings-button">
|
||||
<svg fill="#8B949E" height="16"
|
||||
style="display: inline-block; user-select: none; vertical-align: text-bottom;"
|
||||
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 0a8.2 8.2 0 0 1 .701.031C9.444.095 9.99.645 10.16 1.29l.288 1.107c.018.066.079.158.212.224.231.114.454.243.668.386.123.082.233.09.299.071l1.103-.303c.644-.176 1.392.021 1.82.63.27.385.506.792.704 1.218.315.675.111 1.422-.364 1.891l-.814.806c-.049.048-.098.147-.088.294.016.257.016.515 0 .772-.01.147.038.246.088.294l.814.806c.475.469.679 1.216.364 1.891a7.977 7.977 0 0 1-.704 1.217c-.428.61-1.176.807-1.82.63l-1.102-.302c-.067-.019-.177-.011-.3.071a5.909 5.909 0 0 1-.668.386c-.133.066-.194.158-.211.224l-.29 1.106c-.168.646-.715 1.196-1.458 1.26a8.006 8.006 0 0 1-1.402 0c-.743-.064-1.289-.614-1.458-1.26l-.289-1.106c-.018-.066-.079-.158-.212-.224a5.738 5.738 0 0 1-.668-.386c-.123-.082-.233-.09-.299-.071l-1.103.303c-.644.176-1.392-.021-1.82-.63a8.12 8.12 0 0 1-.704-1.218c-.315-.675-.111-1.422.363-1.891l.815-.806c.05-.048.098-.147.088-.294a6.214 6.214 0 0 1 0-.772c.01-.147-.038-.246-.088-.294l-.815-.806C.635 6.045.431 5.298.746 4.623a7.92 7.92 0 0 1 .704-1.217c.428-.61 1.176-.807 1.82-.63l1.102.302c.067.019.177.011.3-.071.214-.143.437-.272.668-.386.133-.066.194-.158.211-.224l.29-1.106C6.009.645 6.556.095 7.299.03 7.53.01 7.764 0 8 0Zm-.571 1.525c-.036.003-.108.036-.137.146l-.289 1.105c-.147.561-.549.967-.998 1.189-.173.086-.34.183-.5.29-.417.278-.97.423-1.529.27l-1.103-.303c-.109-.03-.175.016-.195.045-.22.312-.412.644-.573.99-.014.031-.021.11.059.19l.815.806c.411.406.562.957.53 1.456a4.709 4.709 0 0 0 0 .582c.032.499-.119 1.05-.53 1.456l-.815.806c-.081.08-.073.159-.059.19.162.346.353.677.573.989.02.03.085.076.195.046l1.102-.303c.56-.153 1.113-.008 1.53.27.161.107.328.204.501.29.447.222.85.629.997 1.189l.289 1.105c.029.109.101.143.137.146a6.6 6.6 0 0 0 1.142 0c.036-.003.108-.036.137-.146l.289-1.105c.147-.561.549-.967.998-1.189.173-.086.34-.183.5-.29.417-.278.97-.423 1.529-.27l1.103.303c.109.029.175-.016.195-.045.22-.313.411-.644.573-.99.014-.031.021-.11-.059-.19l-.815-.806c-.411-.406-.562-.957-.53-1.456a4.709 4.709 0 0 0 0-.582c-.032-.499.119-1.05.53-1.456l.815-.806c.081-.08.073-.159.059-.19a6.464 6.464 0 0 0-.573-.989c-.02-.03-.085-.076-.195-.046l-1.102.303c-.56.153-1.113.008-1.53-.27a4.44 4.44 0 0 0-.501-.29c-.447-.222-.85-.629-.997-1.189l-.289-1.105c-.029-.11-.101-.143-.137-.146a6.6 6.6 0 0 0-1.142 0ZM11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0ZM9.5 8a1.5 1.5 0 1 0-3.001.001A1.5 1.5 0 0 0 9.5 8Z">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div aria-label="Join the Discord server for rapid support." class="btn btn-sm ml-3 tooltipped tooltipped-ne mb-1"
|
||||
id="smbtn1">
|
||||
|
||||
<svg fill="#8B949E" height="16" style="display: inline-block; vertical-align: text-bottom;" viewBox="0 0 16 16">
|
||||
<path d="M13.545 2.907a13.2 13.2 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.2 12.2 0 0 0-3.658 0 8 8 0 0 0-.412-.833.05.05 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.04.04 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032q.003.022.021.037a13.3 13.3 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019q.463-.63.818-1.329a.05.05 0 0 0-.01-.059l-.018-.011a9 9 0 0 1-1.248-.595.05.05 0 0 1-.02-.066l.015-.019q.127-.095.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.05.05 0 0 1 .053.007q.121.1.248.195a.05.05 0 0 1-.004.085 8 8 0 0 1-1.249.594.05.05 0 0 0-.03.03.05.05 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.2 13.2 0 0 0 4.001-2.02.05.05 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.03.03 0 0 0-.02-.019m-8.198 7.307c-.789 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612m5.316 0c-.788 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612"/>
|
||||
</svg>
|
||||
Discord
|
||||
</div>
|
||||
|
||||
<div aria-label="Visit the SteamDB instant search website."
|
||||
class="btn btn-sm ml-2 tooltipped tooltipped-n mb-1" id="smbtn2">
|
||||
<svg aria-hidden="true" class="octicon" fill="#8B949E" height="16" viewBox="0 0 128 128" width="16"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M63.9 0C30.5 0 3.1 11.9.1 27.1l35.6 6.7c2.9-.9 6.2-1.3 9.6-1.3l16.7-10c-.2-2.5 1.3-5.1 4.7-7.2 4.8-3.1 12.3-4.8 19.9-4.8 5.2-.1 10.5.7 15 2.2 11.2 3.8 13.7 11.1 5.7 16.3-5.1 3.3-13.3 5-21.4 4.8l-22 7.9c-.2 1.6-1.3 3.1-3.4 4.5-5.9 3.8-17.4 4.7-25.6 1.9-3.6-1.2-6-3-7-4.8L2.5 38.4c2.3 3.6 6 6.9 10.8 9.8C5 53 0 59 0 65.5c0 6.4 4.8 12.3 12.9 17.1C4.8 87.3 0 93.2 0 99.6 0 115.3 28.6 128 64 128c35.3 0 64-12.7 64-28.4 0-6.4-4.8-12.3-12.9-17 8.1-4.8 12.9-10.7 12.9-17.1 0-6.5-5-12.6-13.4-17.4 8.3-5.1 13.3-11.4 13.3-18.2 0-16.5-28.7-29.9-64-29.9zm22.8 14.2c-5.2.1-10.2 1.2-13.4 3.3-5.5 3.6-3.8 8.5 3.8 11.1 7.6 2.6 18.1 1.8 23.6-1.8s3.8-8.5-3.8-11c-3.1-1-6.7-1.5-10.2-1.5zm.3 1.7c7.4 0 13.3 2.8 13.3 6.2 0 3.4-5.9 6.2-13.3 6.2s-13.3-2.8-13.3-6.2c0-3.4 5.9-6.2 13.3-6.2zM45.3 34.4c-1.6.1-3.1.2-4.6.4l9.1 1.7a10.8 5 0 1 1-8.1 9.3l-8.9-1.7c1 .9 2.4 1.7 4.3 2.4 6.4 2.2 15.4 1.5 20-1.5s3.2-7.2-3.2-9.3c-2.6-.9-5.7-1.3-8.6-1.3zM109 51v9.3c0 11-20.2 19.9-45 19.9-24.9 0-45-8.9-45-19.9v-9.2c11.5 5.3 27.4 8.6 44.9 8.6 17.6 0 33.6-3.3 45.2-8.7zm0 34.6v8.8c0 11-20.2 19.9-45 19.9-24.9 0-45-8.9-45-19.9v-8.8c11.6 5.1 27.4 8.2 45 8.2s33.5-3.1 45-8.2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
SteamDB
|
||||
</div>
|
||||
|
||||
<div aria-label="Donate to the author of SteamDepotDownloaderGUI."
|
||||
class="btn btn-sm ml-2 tooltipped tooltipped-n mb-1" id="smbtn3">
|
||||
<svg fill="#8B949E" height="16" style="display: inline-block; vertical-align: text-bottom;"
|
||||
viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M2 2.75A2.75 2.75 0 0 1 4.75 0c.983 0 1.873.42 2.57 1.232.268.318.497.668.68 1.042.183-.375.411-.725.68-1.044C9.376.42 10.266 0 11.25 0a2.75 2.75 0 0 1 2.45 4h.55c.966 0 1.75.784 1.75 1.75v2c0 .698-.409 1.301-1 1.582v4.918A1.75 1.75 0 0 1 13.25 16H2.75A1.75 1.75 0 0 1 1 14.25V9.332C.409 9.05 0 8.448 0 7.75v-2C0 4.784.784 4 1.75 4h.55c-.192-.375-.3-.8-.3-1.25ZM7.25 9.5H2.5v4.75c0 .138.112.25.25.25h4.5Zm1.5 0v5h4.5a.25.25 0 0 0 .25-.25V9.5Zm0-4V8h5.5a.25.25 0 0 0 .25-.25v-2a.25.25 0 0 0-.25-.25Zm-7 0a.25.25 0 0 0-.25.25v2c0 .138.112.25.25.25h5.5V5.5h-5.5Zm3-4a1.25 1.25 0 0 0 0 2.5h2.309c-.233-.818-.542-1.401-.878-1.793-.43-.502-.915-.707-1.431-.707ZM8.941 4h2.309a1.25 1.25 0 0 0 0-2.5c-.516 0-1 .205-1.43.707-.337.392-.646.975-.879 1.793Z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Donate
|
||||
</div>
|
||||
|
||||
<div aria-label="View the official SteamDepotDownloaderGUI tutorials."
|
||||
class="btn btn-sm ml-2 tooltipped tooltipped-nw mb-1" id="smbtn4">
|
||||
<svg fill="#8B949E" style="display: inline-block; vertical-align: text-bottom;" height="16" width="16">
|
||||
<path d="M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0 1 14.25 14H1.75A1.75 1.75 0 0 1 0 12.25Zm1.75-.25a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25Z"></path>
|
||||
<path d="M6 10.559V5.442a.25.25 0 0 1 .379-.215l4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559Z"></path>
|
||||
</svg>
|
||||
Tutorial
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2" id="warning-banners">
|
||||
<div hidden id="dotnetwarning">
|
||||
<div class="flash flash-error mx-2 mt-2 color-shadow-medium" id="dotnetalert">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
<code><span class="text-italic">dotnet</span></code> was not found.
|
||||
<button class="btn btn-sm flash-action" id="dotnetalertbtn">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.47 10.78a.75.75 0 001.06 0l3.75-3.75a.75.75 0 00-1.06-1.06L8.75 8.44V1.75a.75.75 0 00-1.5
|
||||
0v6.69L4.78 5.97a.75.75 0 00-1.06 1.06l3.75 3.75zM3.75 13a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
<span class="text-bold">Download</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div hidden id="emptywarning">
|
||||
<div class="flash flash-warn mx-2 mt-2 color-shadow-medium" id="emptyalert">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Please fill in all required fields.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flash mx-2 mt-2 color-shadow-medium" hidden id="downloadingnotice">
|
||||
<svg class="octicon" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"
|
||||
fill-rule="evenodd"></path>
|
||||
</svg>
|
||||
Downloading and extracting DepotDownloader<span class="AnimatedEllipsis"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="settings-surrounding" id="settings-surrounding">
|
||||
<div class="settings-content mx-auto" id="settings-content">
|
||||
<label class="version-info" id="version-info">UNKNOWN</label>
|
||||
<button class="opium-button" id="opium-btn">00pium</button>
|
||||
<h2><b>Settings</b></h2>
|
||||
<hr>
|
||||
<h4><b>Appearance</b></h4>
|
||||
<div class="form-group">
|
||||
<div class="form-group-header">
|
||||
<label>Theme</label>
|
||||
</div>
|
||||
<div class="form-group-body">
|
||||
<div class="BtnGroup">
|
||||
<button aria-selected="true" class="BtnGroup-item btn btn-sm" id="theme-auto" type="button">
|
||||
Auto
|
||||
</button>
|
||||
<button class="BtnGroup-item btn btn-sm" id="theme-light" type="button">
|
||||
Light
|
||||
</button>
|
||||
<button class="BtnGroup-item btn btn-sm" id="theme-dark" type="button">
|
||||
Dark
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<h4><b>Output</b></h4>
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="form-group-header">
|
||||
<label for="folder-name-custom-input">Game directory name</label>
|
||||
</div>
|
||||
<div class="form-group-body">
|
||||
<div class="BtnGroup">
|
||||
<button aria-selected="true" class="BtnGroup-item btn btn-sm" id="folder-name-appid"
|
||||
type="button">
|
||||
Manifest ID
|
||||
</button>
|
||||
<button class="BtnGroup-item btn btn-sm" id="folder-name-custom" type="button">
|
||||
Custom
|
||||
</button>
|
||||
</div>
|
||||
<br>
|
||||
<input class="form-control input-block mt-2" disabled
|
||||
id="folder-name-custom-input" placeholder="DepotDownloader output directory name"
|
||||
type="text">
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<h4><b>Debugging</b></h4>
|
||||
<div class="form-group">
|
||||
<div class="form-group-header">
|
||||
<label for="terminal-dropdown">Linux only: Force a terminal</label>
|
||||
</div>
|
||||
<div class="form-group-body">
|
||||
<div class="mb-2">
|
||||
<select class="form-select" id="terminal-dropdown">
|
||||
<!-- "(not installed)" part is sliced later. -->
|
||||
<option disabled>GNOME Terminal (not installed)</option>
|
||||
<option disabled>Alacritty (not installed)</option>
|
||||
<option disabled>Konsole (not installed)</option>
|
||||
<option disabled>GNOME Console (not installed)</option>
|
||||
<option disabled>Xfce Terminal (not installed)</option>
|
||||
<option disabled>Deepin Terminal (not installed)</option>
|
||||
<option disabled>Terminator (not installed)</option>
|
||||
<option disabled>Terminology (not installed)</option>
|
||||
<option disabled>Kitty (not installed)</option>
|
||||
<option disabled>LXTerminal (not installed)</option>
|
||||
<option disabled>Tilix (not installed)</option>
|
||||
<option disabled>cool-retro-term (not installed)</option>
|
||||
<option disabled>XTerm (not installed)</option>
|
||||
<option disabled>CMD (not installed)</option>
|
||||
<option disabled>macOS Terminal (not installed)</option>
|
||||
<option selected="selected">Auto</option>
|
||||
</select>
|
||||
<br>
|
||||
found: <span class="Counter"><code id="terminals-found">none</code></span>
|
||||
<br>default: <span class="Counter"><code id="default-terminal">none</code></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
215
src/ts/main.ts
Normal file
@@ -0,0 +1,215 @@
|
||||
import $ from "jquery";
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import {open as openDialog} from "@tauri-apps/plugin-dialog";
|
||||
import {open as openShell} from "@tauri-apps/plugin-shell";
|
||||
import {listen} from "@tauri-apps/api/event";
|
||||
|
||||
function setLoader(state: boolean) {
|
||||
$("#busy").prop("hidden", !state);
|
||||
}
|
||||
|
||||
|
||||
function setLoadingState(state: boolean) {
|
||||
$("#busy").prop("hidden", !state);
|
||||
|
||||
// loop through all buttons and input fields and disable them
|
||||
for (const element of document.querySelectorAll("button, input")) {
|
||||
if (element.closest("#settings-content")) continue;
|
||||
(element as any).disabled = state;
|
||||
}
|
||||
|
||||
// These elements need additional properties to be properly disabled
|
||||
$("#pickpath").prop("ariaDisabled", state);
|
||||
$("#downloadbtn").prop("ariaDisabled", state);
|
||||
|
||||
// disable internet buttons
|
||||
for (const element of document.querySelectorAll("#internet-btns div")) {
|
||||
element.ariaDisabled = String(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Returns list of IDs of invalid form fields
|
||||
const invalidFields = () => {
|
||||
const form = document.forms[0];
|
||||
|
||||
const invalidFields: string[] = [];
|
||||
for (const input of form) {
|
||||
const inputElement = input as HTMLInputElement;
|
||||
const valid = !(inputElement.value === "" && inputElement?.parentElement?.classList.contains("required"));
|
||||
if (!valid) {
|
||||
invalidFields.push(inputElement.id);
|
||||
}
|
||||
}
|
||||
// console.debug(`[${invalidFields.join(", ")}] fields invalid/empty`);
|
||||
|
||||
return invalidFields;
|
||||
};
|
||||
|
||||
|
||||
$(async () => {
|
||||
let terminalsCollected = false;
|
||||
let downloadDirectory: string | null;
|
||||
|
||||
// Startup logic
|
||||
setLoadingState(true);
|
||||
|
||||
await invoke("preload_vectum");
|
||||
|
||||
setLoadingState(false);
|
||||
|
||||
|
||||
// Collect the rest of the terminals in the background.
|
||||
if (!terminalsCollected) {
|
||||
setLoader(true);
|
||||
// @ts-ignore
|
||||
const terminals = await invoke("get_all_terminals") as string[];
|
||||
for (const terminal in terminals) {
|
||||
console.log(terminal);
|
||||
}
|
||||
|
||||
// Allow opening settings now that it is ready to be shown.
|
||||
$("#settings-button").prop("ariaDisabled", false);
|
||||
terminalsCollected = true;
|
||||
setLoader(false);
|
||||
}
|
||||
|
||||
$("#pickpath").on("click", async () => {
|
||||
// Open a dialog
|
||||
downloadDirectory = await openDialog({
|
||||
title: "Choose where to download the game. You can specify the directory later.",
|
||||
multiple: false,
|
||||
directory: true,
|
||||
canCreateDirectories: true
|
||||
});
|
||||
|
||||
if (downloadDirectory == null) {
|
||||
// user cancelled
|
||||
$("#checkpath").prop("ariaDisabled", true);
|
||||
$("#checkpath").prop("disabled", true);
|
||||
return;
|
||||
}
|
||||
|
||||
$("#checkpath").prop("ariaDisabled", false);
|
||||
$("#checkpath").prop("disabled", false);
|
||||
|
||||
console.log(downloadDirectory);
|
||||
});
|
||||
|
||||
$("#checkpath").on("click", async () => {
|
||||
console.log(`Checking path: ${downloadDirectory}`);
|
||||
|
||||
if (downloadDirectory != null) {
|
||||
await openShell(downloadDirectory);
|
||||
} else {
|
||||
$("#checkpath").prop("ariaDisabled", true);
|
||||
}
|
||||
});
|
||||
|
||||
$("#downloadbtn").on("click", async () => {
|
||||
console.log("download button clicked");
|
||||
|
||||
if (invalidFields().length > 0) {
|
||||
// Loop through invalid fields. If there are any, make those "errored" and block the download button.
|
||||
for (const id of invalidFields()) {
|
||||
document.getElementById(id)?.parentElement?.classList.toggle("errored", true);
|
||||
$("#emptywarning").prop("hidden", false);
|
||||
$("#downloadbtn").prop("ariaDisabled", true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setLoadingState(true);
|
||||
$("#downloadingnotice").prop("hidden", false);
|
||||
$("#busy").prop("hidden", true); // Don't show the loader this time.
|
||||
|
||||
const terminalChoice = (document.getElementById("terminal-dropdown") as HTMLSelectElement).selectedIndex;
|
||||
const directoryNameChoice = $("#folder-name-custom-input").val();
|
||||
|
||||
|
||||
// Output path w/ directories chosen is: {downloadDirectory}/{directoryNameChoice}
|
||||
const vectumOptions = {
|
||||
terminal: terminalChoice == 15 ? null : terminalChoice,
|
||||
output_directory: downloadDirectory || null, // if not specified let backend choose a path.
|
||||
directory_name: directoryNameChoice || null,
|
||||
};
|
||||
|
||||
const steamDownload = {
|
||||
// String || null translate to Some(String) || None
|
||||
username: String($("#username").val()).trim() || null,
|
||||
password: String($("#password").val()).trim() || null,
|
||||
app_id: $("#appid").val(),
|
||||
depot_id: $("#depotid").val(),
|
||||
manifest_id: $("#manifestid").val(),
|
||||
options: vectumOptions
|
||||
};
|
||||
|
||||
// console.debug(steamDownload);
|
||||
await invoke("download_depotdownloader");
|
||||
|
||||
$("#downloadingnotice").prop("hidden", true);
|
||||
setLoadingState(false);
|
||||
|
||||
console.debug("DepotDownloader download process completed. Starting game download...");
|
||||
|
||||
await invoke("start_download", {steamDownload: steamDownload});
|
||||
console.log("Send frontend data over to backend. Ready for next download.");
|
||||
});
|
||||
|
||||
$("#settings-button").on("click", async () => {
|
||||
if (terminalsCollected) $("#settings-surrounding").css("display", "block");
|
||||
});
|
||||
|
||||
$("#settings-surrounding").on("click", (event) => {
|
||||
if (event.target === document.getElementById("settings-surrounding")) {
|
||||
$("#settings-surrounding").css("display", "none");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
$("#opium-btn").on("click", () => {
|
||||
openShell("https://00pium.net");
|
||||
});
|
||||
|
||||
|
||||
document.forms[0].addEventListener("input", (event) => {
|
||||
// Remove errored class. This is a bad way to do it, but it works for now.
|
||||
const target = event.target as HTMLElement;
|
||||
target?.parentElement?.classList.toggle("errored", false);
|
||||
|
||||
// If there are no more invalid fields, hide the warning and enable the download button again
|
||||
if (invalidFields().length === 0) {
|
||||
$("#emptywarning").prop("hidden", true);
|
||||
$("#downloadbtn").prop("ariaDisabled", false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
let a = 0;
|
||||
// Each terminal that is installed gets received from rust with this event.
|
||||
listen<[number, number]>("working-terminal", (event) => {
|
||||
a++;
|
||||
console.log(
|
||||
`Terminal #${event.payload[0]} is installed. a = ${a}`
|
||||
);
|
||||
const terminalSelection = (document.getElementById("terminal-dropdown") as HTMLSelectElement);
|
||||
|
||||
// Enable the <option> of the terminal because we know it is available. Ignore null check because we know it is valid.
|
||||
// @ts-ignore
|
||||
terminalSelection.options.item(event.payload[0]).disabled = false;
|
||||
// @ts-ignore 16
|
||||
|
||||
terminalSelection.options.item(event.payload[0]).text = terminalSelection.options.item(event.payload[0]).text.slice(0,-16);
|
||||
|
||||
$("#terminals-found").text(`${a}/${event.payload[1]}`);
|
||||
});
|
||||
|
||||
|
||||
listen<string>("default-terminal", (event) => {
|
||||
console.log(
|
||||
`Default terminal is ${event.payload}.`
|
||||
);
|
||||
|
||||
$("#default-terminal").text(event.payload);
|
||||
});
|
||||
34
src/ts/preload.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {message} from "@tauri-apps/plugin-dialog";
|
||||
import {invoke} from "@tauri-apps/api/core";
|
||||
import {open} from "@tauri-apps/plugin-shell";
|
||||
import $ from "jquery";
|
||||
|
||||
|
||||
$(async () => {
|
||||
/* eslint-disable indent */
|
||||
switch (await invoke("internet_connection")) {
|
||||
case false: {
|
||||
await message("No internet connection! Can't proceed.", {
|
||||
title: "SteamDepotDownloaderGUI", kind: "error", okLabel: "Close"
|
||||
});
|
||||
}
|
||||
}
|
||||
/* eslint-enable indent */
|
||||
|
||||
//discord
|
||||
$("#smbtn1").on("click", () => {
|
||||
open("https://discord.com/invite/3qCt4DT5qe");
|
||||
});
|
||||
// steamdb
|
||||
$("#smbtn2").on("click", () => {
|
||||
open("https://steamdb.info/instantsearch");
|
||||
});
|
||||
// donate
|
||||
$("#smbtn3").on("click", () => {
|
||||
open("https://paypal.me/onderkin");
|
||||
});
|
||||
// tutorial
|
||||
$("#smbtn4").on("click", () => {
|
||||
open("https://youtube.com/playlist?list=PLRAjc5plLScj967hnsYX-I3Vjw9C1v7Ca");
|
||||
});
|
||||
});
|
||||
46
src/ts/settings.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import {getVersion} from "@tauri-apps/api/app";
|
||||
import {open} from "@tauri-apps/plugin-shell";
|
||||
import $ from "jquery";
|
||||
|
||||
|
||||
$(async () => {
|
||||
$("#version-info").text(`v${await getVersion()}`);
|
||||
|
||||
$("#theme-auto").on("click", () => {
|
||||
setTheme("auto");
|
||||
});
|
||||
$("#theme-light").on("click", () => {
|
||||
setTheme("light");
|
||||
});
|
||||
$("#theme-dark").on("click", () => {
|
||||
setTheme("dark");
|
||||
});
|
||||
|
||||
$("#folder-name-appid").on("click", () => {
|
||||
$("#folder-name-custom").attr("aria-selected", "false");
|
||||
$("#folder-name-appid").attr("aria-selected", "true");
|
||||
$("#folder-name-custom-input").prop("disabled", true);
|
||||
$("#folder-name-custom-input").val("");
|
||||
});
|
||||
|
||||
// todo: fix folder-name-custom-input not disabled on untouched app state
|
||||
|
||||
$("#folder-name-custom").on("click", () => {
|
||||
$("#folder-name-appid").attr("aria-selected", "false");
|
||||
$("#folder-name-custom").attr("aria-selected", "true");
|
||||
$("#folder-name-custom-input").prop("disabled", false);
|
||||
});
|
||||
|
||||
console.log(await getVersion());
|
||||
|
||||
$("#version-info").on("click", async () => {
|
||||
await open(`https://github.com/mmvanheusden/SteamDepotDownloaderGUI/releases/v${await getVersion()}`);
|
||||
});
|
||||
});
|
||||
|
||||
function setTheme(theme: string) {
|
||||
$("#theme-auto").attr("aria-selected", String(theme === "auto"));
|
||||
$("#theme-light").attr("aria-selected", String(theme === "light"));
|
||||
$("#theme-dark").attr("aria-selected", String(theme === "dark"));
|
||||
$("#theme").attr("data-color-mode", theme);
|
||||
}
|
||||
68
style.css
@@ -1,68 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'Hubot Sans';
|
||||
src: url('Hubot-Sans.woff2') format('woff2 supports variations'),
|
||||
url('Hubot-Sans.woff2') format('woff2-variations');
|
||||
font-weight: 700;
|
||||
font-stretch: expanded;
|
||||
}
|
||||
|
||||
.f1-light {
|
||||
font-family: 'Hubot Sans', sans-serif;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* The grey part */
|
||||
.settings-surrounding {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: rgba(0, 0, 0, 0.33);
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
overflow: auto;
|
||||
/*noinspection CssUnresolvedCustomProperty*/
|
||||
background-color: var(--bgColor-default, var(--color-canvas-default));
|
||||
margin: 5%;
|
||||
padding: 25px;
|
||||
border: 1px solid #b4dede;
|
||||
width: 90vw; /* 90vw -> 90% */
|
||||
height: 90vh; /* 90vh -> 90% */
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), 0 6px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
background: #0d1117 linear-gradient(to right, #0d1117, #ccc, #0d1117);
|
||||
}
|
||||
[data-color-mode="light"] hr {
|
||||
filter: invert(1);
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
[data-color-mode="auto"] hr {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.version-info {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
font-size: 0.8em;
|
||||
padding: 5px 10px; /* space around text */
|
||||
font-family: monospace;
|
||||
}
|
||||
27
tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": [
|
||||
"ES2020",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"skipLibCheck": true,
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
391
utils.js
@@ -1,391 +0,0 @@
|
||||
var defaultTerminal = ""
|
||||
|
||||
/**
|
||||
* Checks if all required fields are filled and if dotnet is installed in the system path.
|
||||
* It returns a promise that resolves to true if dotnet is installed and all required fields are filled, false otherwise.
|
||||
*
|
||||
* @returns {Promise<unknown>} A promise that resolves to true if dotnet is installed and all required fields are filled, false otherwise.
|
||||
*
|
||||
* **rejects**:
|
||||
* `emptyField` -> One or more required field(s) are not filled in.
|
||||
* `noDotnet` -> `dotnet` has not been found in the path.
|
||||
*/
|
||||
function preDownloadCheck() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Check if all fields are filled
|
||||
const formInputs = document.forms["theform"]
|
||||
|
||||
let unfilledFields = 0
|
||||
for (const input of formInputs) {
|
||||
const isInvalid = input.value === "" && input.parentElement.classList.contains("required")
|
||||
input.parentElement.classList.toggle("errored", isInvalid) // toggle the 'errored' class depending on if isInvalid is true or false.
|
||||
if (isInvalid) unfilledFields++
|
||||
}
|
||||
if (unfilledFields > 0) {
|
||||
reject("emptyField")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if dotnet is found, depending on the platform
|
||||
if (process.platform.toString().includes("win")) {
|
||||
// Windows
|
||||
const {exec} = require("child_process")
|
||||
const command = "dotnet.exe --version"
|
||||
exec(command, function (error) {
|
||||
if (error) {
|
||||
reject("noDotnet")
|
||||
} else {
|
||||
resolve(true)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// Others
|
||||
// macOS seems to be broken.
|
||||
const {exec} = require("child_process")
|
||||
const command = "dotnet --version"
|
||||
exec(command, function (error) {
|
||||
if (error) {
|
||||
reject("noDotnet")
|
||||
} else {
|
||||
resolve(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Downloads a file from a given URL and saves it to the current directory.
|
||||
*
|
||||
* @param {string} url - The URL of the file to download.
|
||||
* @returns {Promise<void>} A promise that resolves when the download is finished, or rejects if an error occurs.
|
||||
*
|
||||
* @throws {Error} If an error occurs during the download or file writing process, the promise is rejected with the error.
|
||||
*/
|
||||
function download(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const {https} = require("follow-redirects") /* Using follow-redirects to follow redirects */
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const file = fs.createWriteStream(platformpath() + path.sep + url.split("/").pop())
|
||||
https.get(url, (response) => {
|
||||
response.pipe(file)
|
||||
file.on("finish", () => {
|
||||
file.close()
|
||||
resolve()
|
||||
})
|
||||
file.on("error", (error) => {
|
||||
console.error(error)
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file from the current directory.
|
||||
*
|
||||
* @param {string} file - The name of the file to be removed.
|
||||
* @returns {Promise<void>} A promise that resolves when the file is successfully removed, or rejects if an error occurs.
|
||||
*
|
||||
* @throws {Error} If an error occurs during the file removal process, the promise is rejected with the error.
|
||||
*/
|
||||
function removeFile(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
fs.unlink(platformpath() + path.sep + file, function (error) {
|
||||
if (error) {
|
||||
reject(error)
|
||||
console.error(error)
|
||||
} else resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a directory from the current directory.
|
||||
*
|
||||
* @param {string} dir - The name of the directory to be removed.
|
||||
* @returns {Promise<void>} A promise that resolves when the directory is successfully removed, or rejects if an error occurs.
|
||||
*
|
||||
* @throws {Error} If an error occurs during the directory removal process, the promise is rejected with the error.
|
||||
*/
|
||||
function removeDir(dir,) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
fs.rm(platformpath() + path.sep + dir, {recursive: true, force: true}, function (error) {
|
||||
if (error) {
|
||||
reject(error)
|
||||
console.error(error)
|
||||
} else resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Unzips a file to a specified target directory.
|
||||
*
|
||||
* @param {string} file - The name of the zip file to be unzipped.
|
||||
* @param {string} target - The target directory where the file will be unzipped to.
|
||||
* @returns {Promise<void>} A promise that resolves when the unzip operation is complete, or rejects if an error occurs.
|
||||
*
|
||||
* @throws {Error} If an error occurs during the unzip operation, the promise is rejected with the error.
|
||||
*/
|
||||
function unzip(file, target) {
|
||||
const {exec} = require("child_process")
|
||||
const path = require("path")
|
||||
let finalPath = platformpath() + path.sep
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (process.platform.toString().includes("win")) {
|
||||
const command = `powershell.exe -Command Expand-Archive -Path '${finalPath + file}' -Destination '${finalPath + target}'`
|
||||
exec(command, function (error) {
|
||||
if (error) {
|
||||
reject(error)
|
||||
console.error(error)
|
||||
} else resolve()
|
||||
})
|
||||
} else {
|
||||
const command = `unzip -o ${platformpath().replaceAll(" ", "\\ ") + path.sep + file} -d ${platformpath().replaceAll(" ", "\\ ") + path.sep + target}${path.sep}`
|
||||
exec(command, function (error) {
|
||||
if (error) {
|
||||
reject(error)
|
||||
console.error(error)
|
||||
} else resolve()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function createCommandWithTerminal(command, terminal, os) {
|
||||
let cmd = ""
|
||||
|
||||
if (terminal === "auto") {
|
||||
terminal = defaultTerminal[0]
|
||||
}
|
||||
// if os is auto, choose the os for us.
|
||||
if (os === "auto") {
|
||||
if (process.platform.toString().includes("win")) {
|
||||
os = 0
|
||||
} else if (process.platform.toString().includes("linux")) {
|
||||
os = 2
|
||||
}
|
||||
} else console.log("OS is manually chosen")
|
||||
/* eslint-disable */
|
||||
if (os === 0) {
|
||||
cmd = `start cmd.exe /k ${command}`
|
||||
} else if (os === 1) {
|
||||
cmd = `osascript -c 'tell application "Terminal" to do script '${command}'`
|
||||
} else if (os === 2) {
|
||||
switch (terminal) {
|
||||
case 0:
|
||||
cmd = `gnome-terminal -e 'bash -c "${command};$SHELL"'`
|
||||
break
|
||||
case 1:
|
||||
cmd = `konsole --hold -e "${command}"`
|
||||
break
|
||||
case 2:
|
||||
cmd = `xfce4-terminal -H -e "${command}"`
|
||||
break
|
||||
case 3:
|
||||
cmd = `terminator -e 'bash -c "${command};$SHELL"'`
|
||||
break
|
||||
case 4:
|
||||
cmd = `terminology -H -e "${command}"`
|
||||
break
|
||||
case 5:
|
||||
cmd = `xterm -hold -T "Downloading Depot..." -e "${command}"`
|
||||
break
|
||||
case 6:
|
||||
cmd = `kitty --hold sh -c "${command}"`
|
||||
break
|
||||
case 7:
|
||||
cmd = `lxterminal -e "${command};$SHELL"`
|
||||
break
|
||||
case 8:
|
||||
cmd = `tilix -e sh -c "${command};$SHELL"`
|
||||
break
|
||||
case 9:
|
||||
cmd = `deepin-terminal -e 'sh -c "${command};$SHELL"'`
|
||||
break
|
||||
case 10:
|
||||
cmd = `cool-retro-term -e sh -c "${command}"`
|
||||
break
|
||||
default:
|
||||
console.log("NO TERMINAL. PANIC.")
|
||||
}
|
||||
} else if (os === 3) {
|
||||
if (process.platform.toString().includes("win")) {
|
||||
console.log(`COPY-PASTE THE FOLLOWING INTO THE TERMINAL:\n\n${command}`)
|
||||
} else console.log(`COPY-PASTE THE FOLLOWING INTO YOUR TERMINAL:\n\n${command}`)
|
||||
cmd = ""
|
||||
}
|
||||
|
||||
return cmd
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
||||
async function generateRunScript(username, password, appid, depotid, manifestid, folderinput, chosenPath) {
|
||||
const path = require("path")
|
||||
const fs = require("fs")
|
||||
const sep = path.sep
|
||||
let foldername = ""
|
||||
|
||||
// allow enormous strings like &$§"&$="§$/"(§NJUIDW>;!%?aQ52V?*['YsDnRy|(+Q 1h6BmnDQp,(Xr& being used as password.
|
||||
// NOT TESTED
|
||||
password = password.replace(/"/g, "\"\"")
|
||||
|
||||
// if either the username or password fields is empty, anonymous login is used
|
||||
let anonymous = username === "" || password === ""
|
||||
|
||||
|
||||
/* put the username and password flags into one string, allowing for anonymous login.
|
||||
if anonymous: false true
|
||||
| |
|
||||
*/
|
||||
let userpass = anonymous ? "" : `-username ${username} -password "${password}"`
|
||||
|
||||
/* if nothing is inputted by the user in the folder input, it will be defaulted to the appid. else to the value */
|
||||
foldername = folderinput.value === "" ? appid : folderinput.value
|
||||
|
||||
/* if the path isn't selected by the user, go for the path the app is located in. else use the path the user chose. */
|
||||
chosenPath = chosenPath === "" ? platformpath() : chosenPath
|
||||
|
||||
let finalPath = (chosenPath + path.sep + foldername)
|
||||
if (process.platform.includes("win")) {
|
||||
if (finalPath.includes(" ")) {
|
||||
console.log("path contains spaces. adding quotes")
|
||||
finalPath = `"${finalPath}"`
|
||||
console.log("Result: " + finalPath)
|
||||
}
|
||||
} else {
|
||||
finalPath = finalPath.replaceAll(" ", "\\ ")
|
||||
}
|
||||
|
||||
/* / or \ if nothing is inputted its appid replaces " " with "\ ", so whitespaces can be in path names.
|
||||
finalpath: ((the path the user chose) + (seperator) + (the folder name the user chose)).replaceAll()
|
||||
*/
|
||||
|
||||
|
||||
/* The structure of a DepotDownloader command:
|
||||
.NET CLI Path to the DepotDownloader dll. Username/pass combination app id depot id manifest id the dir chosen by user or app path controls how much download servers and threads are used. Needs benchmark TODO
|
||||
| | | | | | | | |
|
||||
dotnet (path)(sep)depotdownloader(sep)DepotDownloader.dll (userpass) -app (appid) -depot (depotid) -manifest (manifestid) -dir (path)(sep) -max-servers 50 -max-downloads 16
|
||||
*/
|
||||
|
||||
if (process.platform.includes("linux")) {
|
||||
// if linux, write a bash script.
|
||||
let content = `#!/usr/bin/env bash
|
||||
dotnet ${platformpath().replaceAll(" ", "\\ ")}${sep}depotdownloader${sep}DepotDownloader.dll ${userpass} -app ${appid} -depot ${depotid} -manifest ${manifestid} -dir ${finalPath}${sep} -max-servers 50 -max-downloads 16
|
||||
`
|
||||
await fs.writeFileSync(`${platformpath()}${sep}run.sh`, content)
|
||||
await fs.chmodSync(`${platformpath()}${sep}run.sh`, "755") // make it executable
|
||||
console.log(`Writing:
|
||||
--------
|
||||
dotnet ${platformpath().replaceAll(" ", "\\ ")}${sep}depotdownloader${sep}DepotDownloader.dll ${userpass} -app ${appid} -depot ${depotid} -manifest ${manifestid} -dir ${finalPath}${sep} -max-servers 50 -max-downloads 16
|
||||
|
||||
to ${platformpath()}${sep}run.sh.
|
||||
`)
|
||||
} else if (process.platform.includes("win")) {
|
||||
// if windows, write a batch script
|
||||
let content = `dotnet "${platformpath()}${sep}depotdownloader${sep}DepotDownloader.dll" ${userpass} -app ${appid} -depot ${depotid} -manifest ${manifestid} -dir ${finalPath} -max-servers 50 -max-downloads 16`
|
||||
await fs.writeFileSync(`${platformpath()}${sep}run.bat`, content)
|
||||
console.log(`Writing:
|
||||
--------
|
||||
dotnet "${platformpath()}${sep}depotdownloader${sep}DepotDownloader.dll" ${userpass} -app ${appid} -depot ${depotid} -manifest ${manifestid} -dir ${finalPath} -max-servers 50 -max-downloads 16
|
||||
|
||||
to ${platformpath()}${sep}run.bat.
|
||||
`)
|
||||
} else { /* macos */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a given command in a separate process.
|
||||
*
|
||||
* @param {string} command - The command to be executed.
|
||||
* @returns {Promise<void>} A promise that resolves when the command execution is successful, or rejects if an error occurs.
|
||||
*
|
||||
* @throws {Error} If an error occurs during the command execution, the promise is rejected with an error message.
|
||||
*/
|
||||
function runCommand(command) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const {exec} = require("child_process")
|
||||
exec(command, function (error) {
|
||||
if (error) {
|
||||
const msg = "Running command failed with error:\n" + error
|
||||
reject(msg)
|
||||
} else resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path where the actual program is being run from, depending on the operating system.
|
||||
* Because __dirname is inconsistent across operating systems, this function is used to get the correct path.
|
||||
*
|
||||
* Windows -> Dev env: __dirname, Portable: process.env.PORTABLE_EXECUTABLE_DIR
|
||||
* Linux -> process.cwd()
|
||||
* @returns {string} The absolute path
|
||||
*/
|
||||
const platformpath = () => {
|
||||
// On linux, it must return process.cwd(). On windows, it must return process.env.PORTABLE_EXECUTABLE_DIR, but only if the program is running from a portable exe.
|
||||
// On linux, __dirname returns the correct path, but on windows, it returns the path to the app.asar file, which is not what we want. Only in dev environment it returns the correct path.
|
||||
|
||||
if (process.platform.includes("win")) {
|
||||
if (process.env.PORTABLE_EXECUTABLE_DIR !== undefined) {
|
||||
return process.env.PORTABLE_EXECUTABLE_DIR
|
||||
} else {
|
||||
return __dirname
|
||||
}
|
||||
} else {
|
||||
return process.cwd()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for the availability of terminal emulators on Linux.
|
||||
* It runs the '--version' command on each terminal emulator and checks if the command is successful.
|
||||
* If the command is successful, it means the terminal emulator is installed and available.
|
||||
* The function returns an array of the indices of the available terminal emulators.
|
||||
* If no terminal emulator is available, it returns false.
|
||||
* If the operating system is not Linux, it also returns false.
|
||||
*
|
||||
* @returns {string[]|boolean} An array of the indices of the available terminal emulators, or false if none are available or the OS is not Linux.
|
||||
*/
|
||||
const forceTerminals = async () => {
|
||||
const commands = [["gnome-terminal", "--version", 0], ["konsole", "--version", 1], ["xfce4-terminal", "--version", 2], ["terminator", "--version", 3], ["terminology", "--version", 4], ["xterm", "-v", 5], ["kitty", "--version", 6], ["lxterminal", "--version", 7], ["tilix", "--version", 8], ["deepin-terminal", "--version", 9], ["cool-retro-term", "--version", 10]]
|
||||
let availableTerminals = [] // list of IDs of terminals, corrospending to their index in the dropdown menu
|
||||
let availableNames = [] // list of names of found terminals
|
||||
if (process.platform === "linux") {
|
||||
for (let i = 0; i < commands.length; i++) {
|
||||
await runCommand(`${commands[i][0]} ${commands[i][1]}`).then(() => {
|
||||
console.log(`Found ${commands[i][0]}`)
|
||||
availableTerminals.push(commands[i][2])
|
||||
availableNames.push(commands[i][0])
|
||||
}).catch(() => {
|
||||
console.log(`${commands[i][0]} not found on system.`)
|
||||
})
|
||||
}
|
||||
if (availableTerminals.length > 0) {
|
||||
defaultTerminal = availableTerminals
|
||||
return [true, availableTerminals, availableNames] // [true, "2,4,7", "twox,fourterm,sevenemulator"] (example)
|
||||
} else return false
|
||||
} else return false
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
preDownloadCheck,
|
||||
download,
|
||||
runCommand,
|
||||
removeDir,
|
||||
removeFile,
|
||||
unzip,
|
||||
platformpath,
|
||||
forceTerminals,
|
||||
generateRunScript,
|
||||
createCommandWithTerminal
|
||||
}
|
||||
35
vite.config.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {defineConfig} from "vite";
|
||||
|
||||
// @ts-expect-error process is a nodejs global
|
||||
const host = process.env.TAURI_DEV_HOST;
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
// 1. prevent vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// 2. tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
host: host || false,
|
||||
hmr: host
|
||||
? {
|
||||
protocol: "ws",
|
||||
host,
|
||||
port: 1421,
|
||||
}
|
||||
: undefined,
|
||||
watch: {
|
||||
// 3. tell vite to ignore watching `src-tauri`
|
||||
ignored: ["**/src-tauri/**"],
|
||||
}
|
||||
},
|
||||
root: "src",
|
||||
build: {
|
||||
outDir: '../dist',
|
||||
emptyOutDir: true,
|
||||
}
|
||||
}));
|
||||