Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: imLinguin/nile
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.1.2
Choose a base ref
...
head repository: imLinguin/nile
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
  • 7 commits
  • 9 files changed
  • 2 contributors

Commits on Sep 27, 2024

  1. feat: allow for forcefully refreshing the library

    this will also be done automatically when logging into another account
    imLinguin committed Sep 27, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    imLinguin Paweł Lidwin
    Copy the full SHA
    63f1c0d View commit details
  2. doc: add usage section

    imLinguin committed Sep 27, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    imLinguin Paweł Lidwin
    Copy the full SHA
    a8c2102 View commit details
  3. fix: make pool size static

    imLinguin committed Sep 27, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    imLinguin Paweł Lidwin
    Copy the full SHA
    42b4541 View commit details

Commits on Sep 28, 2024

  1. ci: use python 3.8 for builds

    this should allow for windows 7 support
    imLinguin committed Sep 28, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    imLinguin Paweł Lidwin
    Copy the full SHA
    75fc576 View commit details

Commits on Jan 20, 2025

  1. [Feat] Add arm64 support (#59)

    Etaash-mathamsetty authored Jan 20, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    8a38ff1 View commit details

Commits on Feb 10, 2025

  1. Verified

    This commit was signed with the committer’s verified signature.
    imLinguin Paweł Lidwin
    Copy the full SHA
    18277f2 View commit details
  2. fix: sort library case in-sensitively

    imLinguin committed Feb 10, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    imLinguin Paweł Lidwin
    Copy the full SHA
    6f3ae6f View commit details
Showing with 53 additions and 40 deletions.
  1. +2 −2 .github/workflows/build.yaml
  2. +20 −8 README.md
  3. +1 −1 nile/api/authorization.py
  4. +9 −2 nile/api/library.py
  5. +1 −1 nile/api/session.py
  6. +1 −0 nile/arguments.py
  7. +2 −2 nile/cli.py
  8. +16 −23 nile/utils/importer.py
  9. +1 −1 nile/utils/uninstall.py
4 changes: 2 additions & 2 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -7,14 +7,14 @@ jobs:
fail-fast: false
max-parallel: 3
matrix:
os: [ubuntu-20.04, macos-12, macos-14, windows-2022]
os: [ubuntu-20.04, ubuntu-22.04-arm, macos-13, macos-14, windows-2022]
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.9'
python-version: '3.8'

- name: Install pyinstaller and dependencies
run: pip3 install --upgrade pyinstaller -r requirements.txt
28 changes: 20 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
</p>

# Nile
Linux native Amazon Games client, based on [this research](https://github.com/Lariaa/GameLauncherResearch/wiki/Amazon-Games)
Cross platform Amazon Games client, based on [this research](https://github.com/Lariaa/GameLauncherResearch/wiki/Amazon-Games)

Nile aims to be CLI and GUI tool for managing and playing games from Amazon.

@@ -26,16 +26,28 @@ Nile aims to be CLI and GUI tool for managing and playing games from Amazon.
## Might not work
- Online games, that use `FuelPump` (I don't have any game like that to test)

# Purpose
## Purpose
This is my attempt to make Amazon Games useful for Linux users, who want to play titles obtained thanks to [Prime](https://prime.amazon.com) membership.

# Dependencies
## Arch and derivatives (Manjaro, Garuda, EndeavourOS)
## Usage

At the moment, Nile is a command line application. If you are looking for graphical user interface, make sure to checkout

- [Heroic Games Launcher](https://heroicgameslauncher.com) - uses Nile as a backend for Amazon Games
- [Lutris](https://lutris.net) - has implementation really similar to what Nile provides

(Recommended) The bundled program is available on the [releases page](https://github.com/imLinguin/nile/releases/latest)

If you wish to run nile from source, see instructions below.


## Dependencies
### Arch and derivatives (Manjaro, Garuda, EndeavourOS)
`sudo pacman -S python-pycryptodome python-zstandard python-requests python-protobuf python-json5`
## Debian and derivatives (Ubuntu, Pop!_OS)
### Debian and derivatives (Ubuntu, Pop!_OS)
`sudo apt install python3-pycryptodome python3-requests python3-zstandard python3-protobuf python3-json5`

## With pip
### With pip
> Do this after cloning the repo and cd into the directory
> Do not install if you installed dependencies through your package manager
@@ -60,7 +72,7 @@ pip install pyinstaller
pyinstaller --onefile --name nile nile/cli.py
```

# Contributing
## Contributing

I'm always open for contributors

@@ -74,7 +86,7 @@ black is used for code formatting
- Run nile `./bin/nile`


# Prior work
## Prior work

This is based on Rodney's work here: https://github.com/derrod/twl.py
Some of his code is implemented in nile, since nothing changed since then in terms of downloading and patching
2 changes: 1 addition & 1 deletion nile/api/authorization.py
Original file line number Diff line number Diff line change
@@ -177,7 +177,7 @@ def handle_redirect(self, redirect):
self.client_id,
self.verifier.decode("utf-8"),
self.serial)
self.library_manager.sync()
self.library_manager.sync(force=True)

def handle_login(self, code, client_id, code_verifier, serial):
"""Handles login through individual args"""
11 changes: 9 additions & 2 deletions nile/api/library.py
Original file line number Diff line number Diff line change
@@ -65,7 +65,7 @@ def _get_sync_request_data(self, serial, next_token=None, sync_point=None):

return request_data

def sync(self):
def sync(self, force=False):
self.logger.info("Synchronizing library")

sync_point = self.config.get("syncpoint", cfg_type=ConfigType.RAW)
@@ -77,6 +77,10 @@ def sync(self):
sync_point = float(sync_point.decode())
self.logger.debug(f"Using sync_point {sync_point}")

if force:
self.logger.warning("Forcefully refreshing the library")
sync_point = None

token, serial = self.config.get(
"user",
[
@@ -110,7 +114,10 @@ def sync(self):
request_data,
)
json_data = response.json()
games.extend(json_data["entitlements"])
new_games = json_data["entitlements"]
if not new_games and sync_point:
self.logger.info("No new games found")
games.extend(new_games)

if not "nextToken" in json_data:
break
2 changes: 1 addition & 1 deletion nile/api/session.py
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ class APIHandler():
def __init__(self, config_manager):
self.config = config_manager
self.session = requests.Session()
adapter = requests.adapters.HTTPAdapter(pool_maxsize=cpu_count())
adapter = requests.adapters.HTTPAdapter(pool_maxsize=12)
self.session.mount("https://", adapter)
self.session.headers.update({
'User-Agent': 'AGSLauncher/1.0.0'
1 change: 1 addition & 0 deletions nile/arguments.py
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ def get_arguments():
"library", help="Your games library is in this place"
)
library_parser.add_argument("sub_command", choices=["list", "sync"])
library_parser.add_argument("--force", "-f", action="store_true", help="Force the action")
library_parser.add_argument(
"--installed", "-i", action="store_true", help="List only installed games"
)
4 changes: 2 additions & 2 deletions nile/cli.py
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ def sort_by_title(self, element):
element["product"].get("title")
if element["product"].get("title") is not None
else ""
)
).lower()

def handle_library(self):
cmd = self.arguments.sub_command
@@ -137,7 +137,7 @@ def handle_library(self):
sys.exit(1)
if self.auth_manager.is_logged_in() and self.auth_manager.is_token_expired():
self.auth_manager.refresh_token()
self.library_manager.sync()
self.library_manager.sync(force=self.arguments.force)

def handle_install(self):
games = self.config.get("library")
39 changes: 16 additions & 23 deletions nile/utils/importer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import logging
from time import sleep
import urllib.parse
from nile.models import manifest
from nile.downloading.worker import DownloadWorker
from nile.utils.config import ConfigType
@@ -19,40 +19,33 @@ def __init__(self, game, folder_path, config, library_manager, session_manager,

self.threads = []

def get_patchmanifest(self):
game_manifest = self.library_manager.get_game_manifest(self.game['id'])

download_url = game_manifest["downloadUrls"][0]
self.logger.debug("Getting protobuff manifest")
response = self.session_manager.session.get(download_url)
r_manifest = manifest.Manifest()
self.logger.debug("Parsing manifest data")
r_manifest.parse(response.content)
self.protobuff_manifest = response.content

self.version = game_manifest["versionId"]
self.download_manager.version = self.version
comparison = manifest.ManifestComparison.compare(
r_manifest
)
return self.download_manager.get_patchmanifest(comparison)
def get_manifest(self):
return self.download_manager.get_manifest()

def stop_threads(self):
self.thpool.shutdown(wait=False, cancel_futures=True)

def verify_integrity(self):
patchmanifest = self.get_patchmanifest()
manifest_data = self.get_manifest()
self.thpool = ThreadPoolExecutor(max_workers=cpu_count())

for file in patchmanifest.files:
local_path = os.path.join(self.folder_path, file.path.replace("\\", os.sep), file.filename)
comparison = manifest.ManifestComparison.compare(
manifest_data, None
)

for file in comparison.new:
local_path = os.path.join(self.folder_path, file.path.replace("\\", os.sep))
self.logger.debug(f"Verifying: {local_path}")
if not os.path.isfile(local_path):
self.stop_threads()
self.logger.error(f"{local_path} is missing or corrupted")
return False

url = urllib.parse.urlparse(self.download_manager.downloadUrl)
url = url._replace(path=url.path + '/files/' + file.hash.value)
url = urllib.parse.urlunparse(url)
worker = DownloadWorker(
url,
file,
local_path,
self.session_manager,
@@ -88,7 +81,7 @@ def finish(self):
# Save manifest to the file

self.config.write(
f"manifests/{self.game['product']['id']}", self.protobuff_manifest, cfg_type=ConfigType.RAW
f"manifests/{self.game['product']['id']}", self.download_manager.protobuff_manifest, cfg_type=ConfigType.RAW
)

# Save data to installed.json file
@@ -98,7 +91,7 @@ def finish(self):
installed_array = list()

installed_game_data = dict(
id=self.game["product"]["id"], version=self.version, path=self.folder_path
id=self.game["product"]["id"], version=self.download_manager.version, path=self.folder_path
)

installed_array.append(installed_game_data)
2 changes: 1 addition & 1 deletion nile/utils/uninstall.py
Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@ def uninstall(self):
os.rmdir(full_path)

# Remove installation directory if it's empty
if not os.listdir(installed_info["path"]):
if os.path.isdir(installed_info["path"]) and not os.listdir(installed_info["path"]):
os.rmdir(installed_info["path"])

self.config.write("installed", installed_games)