redfetch 1.2.0__tar.gz → 1.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {redfetch-1.2.0 → redfetch-1.4.0}/.github/workflows/pipeline.yml +65 -16
- {redfetch-1.2.0 → redfetch-1.4.0}/.gitignore +4 -0
- redfetch-1.4.0/AGENTS.md +21 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/CHANGELOG.md +17 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/PKG-INFO +38 -16
- {redfetch-1.2.0 → redfetch-1.4.0}/README.md +23 -2
- {redfetch-1.2.0 → redfetch-1.4.0}/pyproject.toml +13 -12
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/__about__.py +2 -2
- redfetch-1.4.0/src/redfetch/api.py +50 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/auth.py +1 -1
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/config.py +213 -103
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/config_firstrun.py +24 -9
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/desktop_shortcut.py +0 -7
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/download.py +199 -36
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/listener.py +16 -4
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/main.py +147 -150
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/meta.py +4 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/navmesh.py +32 -98
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/net.py +1 -10
- redfetch-1.4.0/src/redfetch/post_update.py +141 -0
- redfetch-1.4.0/src/redfetch/processes.py +290 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/push.py +2 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/runtime_errors.py +1 -3
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/settings.toml +15 -5
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/special.py +2 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/store.py +11 -15
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/sync.py +98 -32
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/sync_discovery.py +19 -42
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/sync_executor.py +2 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/sync_planner.py +3 -3
- redfetch-1.4.0/src/redfetch/sync_remote.py +135 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/sync_types.py +53 -13
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/terminal_ui.py +496 -395
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/terminal_ui.tcss +31 -79
- redfetch-1.4.0/src/redfetch/update_status.py +62 -0
- redfetch-1.4.0/src/redfetch/utils.py +310 -0
- redfetch-1.4.0/tests/test_check.py +144 -0
- redfetch-1.4.0/tests/test_close_gating.py +787 -0
- redfetch-1.4.0/tests/test_env_selection.py +164 -0
- redfetch-1.4.0/tests/test_fetch_sync_info.py +67 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/tests/test_first_run_setup.py +20 -39
- redfetch-1.4.0/tests/test_licensed_resources_filtering.py +83 -0
- redfetch-1.4.0/tests/test_process_lock.py +48 -0
- redfetch-1.4.0/tests/test_remote_snapshot.py +97 -0
- redfetch-1.4.0/tests/test_settings_local_deltas.py +56 -0
- redfetch-1.4.0/tests/test_staged_extraction.py +340 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/tests/test_sync_pipeline_regressions.py +65 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/tests/test_sync_queue_rules.py +29 -8
- redfetch-1.2.0/src/redfetch/ResourceInstallBtn.js +0 -237
- redfetch-1.2.0/src/redfetch/api.py +0 -135
- redfetch-1.2.0/src/redfetch/processes.py +0 -106
- redfetch-1.2.0/src/redfetch/sync_remote.py +0 -179
- redfetch-1.2.0/src/redfetch/unloadmq.py +0 -148
- redfetch-1.2.0/src/redfetch/utils.py +0 -149
- redfetch-1.2.0/tests/test_cancellation_e2e.py +0 -123
- redfetch-1.2.0/tests/test_licensed_resources_filtering.py +0 -238
- redfetch-1.2.0/tests/test_publish_message_file_fallback.py +0 -158
- redfetch-1.2.0/tests/test_special_resources_e2e.py +0 -506
- redfetch-1.2.0/tests/test_uninstall_e2e.py +0 -233
- {redfetch-1.2.0 → redfetch-1.4.0}/LICENSE +0 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/redfetch.ico +0 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/__init__.py +0 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/src/redfetch/detecteq.py +0 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/tests/test_is_special_or_dependency.py +0 -0
- {redfetch-1.2.0 → redfetch-1.4.0}/tests/test_path_reset.py +0 -0
|
@@ -60,6 +60,13 @@ jobs:
|
|
|
60
60
|
run: |
|
|
61
61
|
hatch build
|
|
62
62
|
|
|
63
|
+
- name: Upload Python distribution
|
|
64
|
+
if: github.ref == 'refs/heads/develop'
|
|
65
|
+
uses: actions/upload-artifact@v7
|
|
66
|
+
with:
|
|
67
|
+
name: python-dist
|
|
68
|
+
path: dist/
|
|
69
|
+
|
|
63
70
|
- name: Get Version
|
|
64
71
|
id: get_version
|
|
65
72
|
run: |
|
|
@@ -89,22 +96,26 @@ jobs:
|
|
|
89
96
|
with:
|
|
90
97
|
fetch-depth: 0 # Ensure full Git history is available for versioning
|
|
91
98
|
|
|
99
|
+
- name: Download Python distribution
|
|
100
|
+
if: github.ref == 'refs/heads/develop'
|
|
101
|
+
uses: actions/download-artifact@v8
|
|
102
|
+
with:
|
|
103
|
+
name: python-dist
|
|
104
|
+
path: dist
|
|
105
|
+
|
|
92
106
|
- name: Set Environment Variables
|
|
93
107
|
id: set_env_vars
|
|
94
108
|
shell: pwsh
|
|
95
109
|
run: |
|
|
96
|
-
# Set static environment variables
|
|
97
|
-
"PYAPP_PROJECT_NAME=redfetch" >> $env:GITHUB_ENV
|
|
98
110
|
"PYAPP_EXEC_MODULE=redfetch.main" >> $env:GITHUB_ENV
|
|
99
111
|
"PYAPP_PASS_LOCATION=1" >> $env:GITHUB_ENV
|
|
100
112
|
|
|
101
|
-
# Conditionally set PYAPP_PIP_EXTRA_ARGS based on the branch or tag
|
|
102
113
|
if ($env:GITHUB_REF -eq 'refs/heads/develop') {
|
|
103
|
-
|
|
114
|
+
"PYAPP_EMBED_WHEEL=1" >> $env:GITHUB_ENV
|
|
104
115
|
} else {
|
|
105
|
-
|
|
116
|
+
"PYAPP_PROJECT_NAME=redfetch" >> $env:GITHUB_ENV
|
|
106
117
|
}
|
|
107
|
-
"PYAPP_PIP_EXTRA_ARGS
|
|
118
|
+
"PYAPP_PIP_EXTRA_ARGS=" >> $env:GITHUB_ENV
|
|
108
119
|
|
|
109
120
|
# Output the values for verification
|
|
110
121
|
Get-Content $env:GITHUB_ENV
|
|
@@ -145,18 +156,23 @@ jobs:
|
|
|
145
156
|
|
|
146
157
|
- name: Build PyApp Executable
|
|
147
158
|
run: |
|
|
148
|
-
$env:
|
|
159
|
+
if ($env:PYAPP_EMBED_WHEEL -eq '1') {
|
|
160
|
+
# Develop embeds the built wheel so its deps resolve from PyPI rather than TestPyPI
|
|
161
|
+
$wheel = Get-ChildItem -Path "$env:GITHUB_WORKSPACE\dist\*.whl" | Select-Object -First 1
|
|
162
|
+
if (-not $wheel) { throw "No redfetch wheel found in dist/" }
|
|
163
|
+
$env:PYAPP_PROJECT_PATH = $wheel.FullName
|
|
164
|
+
Remove-Item Env:PYAPP_PROJECT_NAME -ErrorAction SilentlyContinue
|
|
165
|
+
Remove-Item Env:PYAPP_PROJECT_VERSION -ErrorAction SilentlyContinue
|
|
166
|
+
}
|
|
167
|
+
|
|
149
168
|
$env:PYAPP_EXEC_MODULE = "${{ env.PYAPP_EXEC_MODULE }}"
|
|
150
169
|
$env:PYAPP_PASS_LOCATION = "${{ env.PYAPP_PASS_LOCATION }}"
|
|
151
170
|
$env:PYAPP_PIP_EXTRA_ARGS = "${{ env.PYAPP_PIP_EXTRA_ARGS }}"
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Write-Host "PYAPP_PROJECT_NAME: $env:PYAPP_PROJECT_NAME"
|
|
171
|
+
|
|
155
172
|
Write-Host "PYAPP_EXEC_MODULE: $env:PYAPP_EXEC_MODULE"
|
|
156
173
|
Write-Host "PYAPP_PASS_LOCATION: $env:PYAPP_PASS_LOCATION"
|
|
157
174
|
Write-Host "PYAPP_PIP_EXTRA_ARGS: $env:PYAPP_PIP_EXTRA_ARGS"
|
|
158
|
-
|
|
159
|
-
|
|
175
|
+
|
|
160
176
|
cargo build --release
|
|
161
177
|
working-directory: .\pyapp-latest
|
|
162
178
|
shell: pwsh
|
|
@@ -184,11 +200,36 @@ jobs:
|
|
|
184
200
|
--set-version-string "InternalName" "redfetch" `
|
|
185
201
|
--set-icon "redfetch.ico"
|
|
186
202
|
|
|
203
|
+
- name: Install Google Cloud KMS CNG Provider
|
|
204
|
+
shell: pwsh
|
|
205
|
+
run: |
|
|
206
|
+
if (Test-Path "$env:SystemRoot\System32\kmscng.dll") {
|
|
207
|
+
Write-Host "kmscng already installed."
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
$url = "https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/cng-v1.3/kmscng-1.3-windows-amd64.zip"
|
|
211
|
+
$zip = Join-Path $env:RUNNER_TEMP "kmscng.zip"
|
|
212
|
+
$dest = Join-Path $env:RUNNER_TEMP "kmscng"
|
|
213
|
+
Invoke-WebRequest $url -OutFile $zip
|
|
214
|
+
Expand-Archive -Path $zip -DestinationPath $dest -Force
|
|
215
|
+
$msi = Get-ChildItem -Path $dest -Recurse -Filter *.msi | Select-Object -First 1
|
|
216
|
+
if (-not $msi) { throw "kmscng MSI not found in archive" }
|
|
217
|
+
Start-Process msiexec.exe -ArgumentList "/i `"$($msi.FullName)`" /qn /norestart" -Wait -NoNewWindow
|
|
218
|
+
if (-not (Test-Path "$env:SystemRoot\System32\kmscng.dll")) { throw "kmscng installation failed" }
|
|
219
|
+
Write-Host "kmscng installed."
|
|
220
|
+
|
|
187
221
|
- name: Sign Executable
|
|
188
222
|
shell: pwsh
|
|
223
|
+
env:
|
|
224
|
+
KMS_KEY: projects/code-signing-rg/locations/global/keyRings/redkeys/cryptoKeys/rgkey/cryptoKeyVersions/1
|
|
225
|
+
SIGNING_CERT: ${{ secrets.SIGNING_CERT }}
|
|
226
|
+
GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}
|
|
189
227
|
run: |
|
|
190
|
-
$
|
|
191
|
-
|
|
228
|
+
$CertFile = Join-Path $env:RUNNER_TEMP "user.crt"
|
|
229
|
+
$KeyFile = Join-Path $env:RUNNER_TEMP "gcp-sa.json"
|
|
230
|
+
[IO.File]::WriteAllBytes($CertFile, [Convert]::FromBase64String($env:SIGNING_CERT))
|
|
231
|
+
[IO.File]::WriteAllText($KeyFile, $env:GCP_SA_KEY)
|
|
232
|
+
$env:GOOGLE_APPLICATION_CREDENTIALS = $KeyFile
|
|
192
233
|
|
|
193
234
|
$signtool = Get-ChildItem -Path "${env:ProgramFiles(x86)}\Windows Kits" -Recurse -Filter "signtool.exe" |
|
|
194
235
|
Where-Object { $_.FullName -match "\\x64\\" } |
|
|
@@ -198,10 +239,18 @@ jobs:
|
|
|
198
239
|
if (-not $signtool) { throw "signtool.exe not found" }
|
|
199
240
|
Write-Host "Using signtool: $signtool"
|
|
200
241
|
|
|
201
|
-
|
|
242
|
+
$tsaServers = @('http://timestamp.digicert.com', 'http://timestamp.sectigo.com')
|
|
243
|
+
$maxAttempts = 5
|
|
244
|
+
for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) {
|
|
245
|
+
$tsa = $tsaServers[($attempt - 1) % $tsaServers.Count]
|
|
246
|
+
& $signtool sign /fd SHA256 /f $CertFile /csp "Google Cloud KMS Provider" /kc $env:KMS_KEY /tr $tsa /td sha256 /v redfetch.exe
|
|
247
|
+
if ($LASTEXITCODE -eq 0) { break }
|
|
248
|
+
if ($attempt -eq $maxAttempts) { throw "Signing failed after $maxAttempts attempts" }
|
|
249
|
+
Start-Sleep -Seconds 2
|
|
250
|
+
}
|
|
202
251
|
& $signtool verify /pa /v redfetch.exe
|
|
203
252
|
|
|
204
|
-
Remove-Item $
|
|
253
|
+
Remove-Item -Force $CertFile, $KeyFile
|
|
205
254
|
|
|
206
255
|
- name: Upload Executable Artifact
|
|
207
256
|
uses: actions/upload-artifact@v7
|
redfetch-1.4.0/AGENTS.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
**redfetch** — a CLI/TUI for downloading and publishing MacroQuest and EverQuest scripts and software via the RedGuides API.
|
|
4
|
+
|
|
5
|
+
## Running tests
|
|
6
|
+
|
|
7
|
+
Use `hatch test` — not `pytest`, `uv`, or an ad-hoc venv
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
hatch test # full suite
|
|
11
|
+
hatch test tests/test_check.py # single file
|
|
12
|
+
```
|
|
13
|
+
## Libraries
|
|
14
|
+
|
|
15
|
+
- [Textual](https://textual.textualize.io/) - TUI
|
|
16
|
+
- [Hatch](https://hatch.pypa.io/latest/) - Build system
|
|
17
|
+
- [PYPA](https://www.pypa.io/en/latest/specifications/) - Package index
|
|
18
|
+
|
|
19
|
+
## Conventions
|
|
20
|
+
|
|
21
|
+
- Use the `dev` environment (`hatch shell dev`) for development
|
|
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.3.0] - 2026-06-26
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `--server` on `update` and `download`: use a different server for one run.
|
|
12
|
+
- TUI: "Also start post-update" — launch EQBCS, MySEQ, or a custom program after a successful update. ([#18](https://github.com/RedGuides/redfetch/issues/18))
|
|
13
|
+
- Staff picks: Ninjadvloot.inc is now on LIVE/TEST/EMU.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Config paths that contain `eqgame.exe` are allowed with a warning instead of being blocked outright. ([#22](https://github.com/RedGuides/redfetch/issues/22))
|
|
17
|
+
- TUI: "Close MQ pre-update" and "Start MQ post-update" are now No / Ask / Yes toggles, so you can pick the prompt-each-time ("Ask") behavior from the UI instead of only on/off.
|
|
18
|
+
- Only settings you've actually changed are written to `settings.local.toml`, keeping the file tidy.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Licensed resources will now download even if you have duplicates. ([#24](https://github.com/RedGuides/redfetch/issues/24))
|
|
22
|
+
- Update checks notice when a resource's subfolder changes.
|
|
23
|
+
- Fixed "RecursionError" on startup by rolling back a dependency.
|
|
24
|
+
|
|
8
25
|
## [1.2.0] - 2026-04-05
|
|
9
26
|
|
|
10
27
|
### Added
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: redfetch
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Download and publish EverQuest scripts and software using the RedGuides API
|
|
5
5
|
Project-URL: Homepage, https://www.redguides.com
|
|
6
6
|
Project-URL: Documentation, https://www.redguides.com/community/resources/redfetch.3177/
|
|
7
7
|
Project-URL: Repository, https://github.com/RedGuides/redfetch
|
|
8
8
|
Project-URL: Issues, https://github.com/RedGuides/redfetch/issues
|
|
9
9
|
Project-URL: Changelog, https://github.com/RedGuides/redfetch/blob/main/CHANGELOG.md
|
|
10
|
-
Project-URL: Source_archive, https://github.com/RedGuides/redfetch/archive/
|
|
10
|
+
Project-URL: Source_archive, https://github.com/RedGuides/redfetch/archive/ccf5cf558b6e6f261fab4079e837d5cb47e7975d.zip
|
|
11
11
|
Author-email: Redbot <ask@redguides.com>
|
|
12
12
|
License-Expression: GPL-3.0-or-later
|
|
13
13
|
License-File: LICENSE
|
|
@@ -17,28 +17,29 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (G
|
|
|
17
17
|
Classifier: Operating System :: OS Independent
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Topic :: Utilities
|
|
20
|
+
Requires-Python: >=3.12
|
|
20
21
|
Requires-Dist: aiofiles~=25.1
|
|
21
|
-
Requires-Dist: aiohttp~=3.
|
|
22
|
+
Requires-Dist: aiohttp~=3.14
|
|
22
23
|
Requires-Dist: aiosqlite~=0.22
|
|
23
|
-
Requires-Dist: cachetools~=7.0
|
|
24
24
|
Requires-Dist: diskcache~=5.6
|
|
25
|
-
Requires-Dist: dynaconf
|
|
25
|
+
Requires-Dist: dynaconf>=3.3.2,~=3.3
|
|
26
|
+
Requires-Dist: filelock~=3.29
|
|
27
|
+
Requires-Dist: hishel[async,httpx]~=1.3
|
|
26
28
|
Requires-Dist: httpx[http2]~=0.28
|
|
27
29
|
Requires-Dist: keepachangelog~=2.0
|
|
28
|
-
Requires-Dist: keyring~=25.
|
|
30
|
+
Requires-Dist: keyring~=25.7
|
|
29
31
|
Requires-Dist: md2bbcode
|
|
30
|
-
Requires-Dist: packaging~=26.
|
|
31
|
-
Requires-Dist: platformdirs~=4.
|
|
32
|
+
Requires-Dist: packaging~=26.2
|
|
33
|
+
Requires-Dist: platformdirs~=4.10
|
|
32
34
|
Requires-Dist: psutil~=7.2
|
|
33
|
-
Requires-Dist: pydantic~=2.
|
|
34
|
-
Requires-Dist: pyperclip~=1.11
|
|
35
|
+
Requires-Dist: pydantic~=2.13
|
|
35
36
|
Requires-Dist: pywin32; sys_platform == 'win32'
|
|
36
|
-
Requires-Dist: rich~=
|
|
37
|
+
Requires-Dist: rich~=15.0
|
|
37
38
|
Requires-Dist: tenacity~=9.1
|
|
38
39
|
Requires-Dist: textual-fspicker~=1.0
|
|
39
|
-
Requires-Dist: textual~=8.
|
|
40
|
-
Requires-Dist: tomlkit~=0.
|
|
41
|
-
Requires-Dist: typer~=0.
|
|
40
|
+
Requires-Dist: textual~=8.2
|
|
41
|
+
Requires-Dist: tomlkit~=0.15
|
|
42
|
+
Requires-Dist: typer~=0.26
|
|
42
43
|
Description-Content-Type: text/markdown
|
|
43
44
|
|
|
44
45
|

|
|
@@ -109,8 +110,10 @@ To run redfetch from the command line:
|
|
|
109
110
|
> ### 📦 Resource Management
|
|
110
111
|
> - `update` - Update all watched and special resources
|
|
111
112
|
> - `--force` / `-f` - Force re-download of all watched resources
|
|
113
|
+
> - `--server` / `-s` - Switch to this server before updating (`LIVE`, `TEST`, `EMU`)
|
|
112
114
|
> - `download <ID_OR_URL>` - Download a specific resource by ID or URL
|
|
113
115
|
> - `--force` / `-f` - Force re-download by resetting this resource's download date
|
|
116
|
+
> - `--server` / `-s` - Switch to this server before downloading (`LIVE`, `TEST`, `EMU`)
|
|
114
117
|
> - `list` - List resources and dependencies in the cache database
|
|
115
118
|
> - `reset` - Reset download dates for watched resources
|
|
116
119
|
>
|
|
@@ -197,12 +200,31 @@ redfetch.exe config SPECIAL_RESOURCES.2218.opt_in false --server TEST
|
|
|
197
200
|
|
|
198
201
|
Then assign the *Very Vanilla MQ* path to your self-compiled MacroQuest.
|
|
199
202
|
|
|
203
|
+
### Custom post-update launch
|
|
204
|
+
|
|
205
|
+
redfetch can launch extra programs after an update completes. Aside from the normal UI toggles, you can add `custom` to `POST_UPDATE_LAUNCH.targets` in `settings.local.toml`, then set `command` to whatever redfetch should run:
|
|
206
|
+
|
|
207
|
+
```toml
|
|
208
|
+
[LIVE.POST_UPDATE_LAUNCH]
|
|
209
|
+
targets = ["custom"]
|
|
210
|
+
command = ['C:\Tools\AfterRedfetch\after-update.exe', '--server', 'LIVE']
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
You can also combine it with the built-in post-update launches. For example, to start EQBCS and MySEQ and run your own Python script:
|
|
214
|
+
|
|
215
|
+
```toml
|
|
216
|
+
[LIVE.POST_UPDATE_LAUNCH]
|
|
217
|
+
targets = ["myseq", "custom", "eqbcs"]
|
|
218
|
+
command = ["C:\\Users\\Public\\Python\\python.exe", "C:\\Users\\Public\\redfetch\\after_update.py"]
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
You can set these per-server, e.g. `[TEST.POST_UPDATE_LAUNCH]`, or global `[DEFAULT.POST_UPDATE_LAUNCH]`.
|
|
222
|
+
|
|
223
|
+
|
|
200
224
|
## Trailmap
|
|
201
225
|
- Add custom buttons for "fetch" tab.
|
|
202
226
|
- Option: Close after update
|
|
203
|
-
- Launch programs with cli options
|
|
204
227
|
- Indicate when updated VV is available
|
|
205
|
-
- Launch more than just mq (eqbcs, etc) upon update.
|
|
206
228
|
- Run from MQ
|
|
207
229
|
- Deeper integration with the forums
|
|
208
230
|
|
|
@@ -66,8 +66,10 @@ To run redfetch from the command line:
|
|
|
66
66
|
> ### 📦 Resource Management
|
|
67
67
|
> - `update` - Update all watched and special resources
|
|
68
68
|
> - `--force` / `-f` - Force re-download of all watched resources
|
|
69
|
+
> - `--server` / `-s` - Switch to this server before updating (`LIVE`, `TEST`, `EMU`)
|
|
69
70
|
> - `download <ID_OR_URL>` - Download a specific resource by ID or URL
|
|
70
71
|
> - `--force` / `-f` - Force re-download by resetting this resource's download date
|
|
72
|
+
> - `--server` / `-s` - Switch to this server before downloading (`LIVE`, `TEST`, `EMU`)
|
|
71
73
|
> - `list` - List resources and dependencies in the cache database
|
|
72
74
|
> - `reset` - Reset download dates for watched resources
|
|
73
75
|
>
|
|
@@ -154,12 +156,31 @@ redfetch.exe config SPECIAL_RESOURCES.2218.opt_in false --server TEST
|
|
|
154
156
|
|
|
155
157
|
Then assign the *Very Vanilla MQ* path to your self-compiled MacroQuest.
|
|
156
158
|
|
|
159
|
+
### Custom post-update launch
|
|
160
|
+
|
|
161
|
+
redfetch can launch extra programs after an update completes. Aside from the normal UI toggles, you can add `custom` to `POST_UPDATE_LAUNCH.targets` in `settings.local.toml`, then set `command` to whatever redfetch should run:
|
|
162
|
+
|
|
163
|
+
```toml
|
|
164
|
+
[LIVE.POST_UPDATE_LAUNCH]
|
|
165
|
+
targets = ["custom"]
|
|
166
|
+
command = ['C:\Tools\AfterRedfetch\after-update.exe', '--server', 'LIVE']
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
You can also combine it with the built-in post-update launches. For example, to start EQBCS and MySEQ and run your own Python script:
|
|
170
|
+
|
|
171
|
+
```toml
|
|
172
|
+
[LIVE.POST_UPDATE_LAUNCH]
|
|
173
|
+
targets = ["myseq", "custom", "eqbcs"]
|
|
174
|
+
command = ["C:\\Users\\Public\\Python\\python.exe", "C:\\Users\\Public\\redfetch\\after_update.py"]
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
You can set these per-server, e.g. `[TEST.POST_UPDATE_LAUNCH]`, or global `[DEFAULT.POST_UPDATE_LAUNCH]`.
|
|
178
|
+
|
|
179
|
+
|
|
157
180
|
## Trailmap
|
|
158
181
|
- Add custom buttons for "fetch" tab.
|
|
159
182
|
- Option: Close after update
|
|
160
|
-
- Launch programs with cli options
|
|
161
183
|
- Indicate when updated VV is available
|
|
162
|
-
- Launch more than just mq (eqbcs, etc) upon update.
|
|
163
184
|
- Run from MQ
|
|
164
185
|
- Deeper integration with the forums
|
|
165
186
|
|
|
@@ -3,6 +3,7 @@ name = "redfetch"
|
|
|
3
3
|
description = "Download and publish EverQuest scripts and software using the RedGuides API"
|
|
4
4
|
readme = "README.md"
|
|
5
5
|
license = "GPL-3.0-or-later"
|
|
6
|
+
requires-python = ">=3.12"
|
|
6
7
|
dynamic = ["version", "urls"]
|
|
7
8
|
authors = [
|
|
8
9
|
{ name = "Redbot", email = "ask@redguides.com" }
|
|
@@ -19,25 +20,25 @@ classifiers = [
|
|
|
19
20
|
dependencies = [
|
|
20
21
|
"aiofiles~=25.1",
|
|
21
22
|
"aiosqlite~=0.22",
|
|
22
|
-
"aiohttp~=3.
|
|
23
|
-
"cachetools~=7.0",
|
|
23
|
+
"aiohttp~=3.14",
|
|
24
24
|
"diskcache~=5.6",
|
|
25
|
-
"dynaconf~=3.2",
|
|
25
|
+
"dynaconf~=3.3,>=3.3.2", # (pr 1409 fixed RecursionError)
|
|
26
|
+
"filelock~=3.29",
|
|
27
|
+
"hishel[httpx,async]~=1.3",
|
|
26
28
|
"httpx[http2]~=0.28",
|
|
27
29
|
"keepachangelog~=2.0",
|
|
28
|
-
"keyring~=25.
|
|
30
|
+
"keyring~=25.7",
|
|
29
31
|
"md2bbcode",
|
|
30
|
-
"packaging~=26.
|
|
31
|
-
"pydantic~=2.
|
|
32
|
-
"platformdirs~=4.
|
|
32
|
+
"packaging~=26.2",
|
|
33
|
+
"pydantic~=2.13",
|
|
34
|
+
"platformdirs~=4.10",
|
|
33
35
|
"psutil~=7.2",
|
|
34
|
-
"
|
|
35
|
-
"rich~=14.3",
|
|
36
|
+
"rich~=15.0",
|
|
36
37
|
"tenacity~=9.1",
|
|
37
|
-
"textual~=8.
|
|
38
|
+
"textual~=8.2",
|
|
38
39
|
"textual-fspicker~=1.0",
|
|
39
|
-
"tomlkit~=0.
|
|
40
|
-
"typer~=0.
|
|
40
|
+
"tomlkit~=0.15",
|
|
41
|
+
"typer~=0.26",
|
|
41
42
|
"pywin32; sys_platform == 'win32'"
|
|
42
43
|
]
|
|
43
44
|
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.
|
|
22
|
-
__version_tuple__ = version_tuple = (1,
|
|
21
|
+
__version__ = version = '1.4.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 4, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Resource API client: fetch_*() takes a client, get_*() is self-contained."""
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
from redfetch import net
|
|
5
|
+
from redfetch.sync_types import SyncInfo
|
|
6
|
+
|
|
7
|
+
BASE_URL = net.BASE_URL
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def fetch_sync_info(client: httpx.AsyncClient) -> SyncInfo:
|
|
11
|
+
"""Per-user sync info"""
|
|
12
|
+
url = f'{BASE_URL}/api/rgsync'
|
|
13
|
+
is_level_2 = is_moderator = False
|
|
14
|
+
watched: set[str] = set()
|
|
15
|
+
licenses: list[dict] = []
|
|
16
|
+
page = 1
|
|
17
|
+
while True:
|
|
18
|
+
data = await net.get_json(client, url, params={'page': page})
|
|
19
|
+
is_level_2 = bool(data.get('is_level_2', False))
|
|
20
|
+
is_moderator = bool(data.get('is_moderator', False))
|
|
21
|
+
watched.update(str(rid) for rid in data.get('watched', []))
|
|
22
|
+
licenses.extend(data.get('licenses', []))
|
|
23
|
+
if not data.get('pagination', {}).get('has_more', False):
|
|
24
|
+
break
|
|
25
|
+
page += 1
|
|
26
|
+
|
|
27
|
+
licensed_ids = {
|
|
28
|
+
str(lic['resource_id']) for lic in licenses if lic.get('resource_id') is not None
|
|
29
|
+
}
|
|
30
|
+
return SyncInfo(
|
|
31
|
+
is_level_2=is_level_2,
|
|
32
|
+
is_moderator=is_moderator,
|
|
33
|
+
watched=watched,
|
|
34
|
+
licensed_ids=licensed_ids,
|
|
35
|
+
licenses=licenses,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def get_sync_info(headers: dict) -> SyncInfo:
|
|
40
|
+
"""fetch_sync_info for callers that hold headers but not a client."""
|
|
41
|
+
async with httpx.AsyncClient(headers=headers, http2=True, timeout=30.0) as client:
|
|
42
|
+
return await fetch_sync_info(client)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def get_resource_details(resource_id: int, headers: dict) -> dict:
|
|
46
|
+
"""Retrieve details of a specific resource from the API."""
|
|
47
|
+
url = f'{BASE_URL}/api/resources/{resource_id}'
|
|
48
|
+
async with httpx.AsyncClient(headers=headers, http2=True, timeout=30.0) as client:
|
|
49
|
+
data = await net.get_json(client, url)
|
|
50
|
+
return data['resource']
|
|
@@ -553,7 +553,7 @@ def initialize_keyring():
|
|
|
553
553
|
raise RuntimeError(
|
|
554
554
|
"No suitable keyring backend found, probably because you're not on Windows.\n\n"
|
|
555
555
|
"Please install `keyrings.alt` by running:\n"
|
|
556
|
-
"
|
|
556
|
+
" pipx inject redfetch keyrings.alt\n\n"
|
|
557
557
|
"Then restart the application."
|
|
558
558
|
)
|
|
559
559
|
except Exception as e:
|