starbash 0.3.0__tar.gz → 0.3.1__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.
- {starbash-0.3.0 → starbash-0.3.1}/PKG-INFO +13 -6
- {starbash-0.3.0 → starbash-0.3.1}/README.md +12 -5
- {starbash-0.3.0 → starbash-0.3.1}/pyproject.toml +1 -1
- starbash-0.3.1/src/starbash/assets/favicon.ico +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/commands/publish.py +16 -9
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/publish/github.py +25 -5
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/publish/github_service.py +54 -2
- starbash-0.3.1/src/starbash/recipes/crop.py +75 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/recipes/osc.py +0 -37
- starbash-0.3.1/src/starbash/templates/report/Gemfile +3 -0
- starbash-0.3.1/src/starbash/templates/report/__init__.py +0 -0
- starbash-0.3.1/src/starbash/templates/report/_config.yml +8 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/templates/report/default.html +2 -1
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/templates/report/target.md.jinja +3 -1
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/tool/starnet.py +25 -4
- {starbash-0.3.0 → starbash-0.3.1}/LICENSE +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/aliases.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/analytics.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/app.py +0 -0
- {starbash-0.3.0/src/starbash/defaults → starbash-0.3.1/src/starbash/assets}/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/check_version.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/commands/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/commands/info.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/commands/process.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/commands/repo.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/commands/select.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/commands/user.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/database.py +0 -0
- {starbash-0.3.0/src/starbash/recipes → starbash-0.3.1/src/starbash/defaults}/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/defaults/starbash.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/doit.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/doit_types.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/dwarf3.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/exception.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/filtering.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/fits.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/icon.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/linux.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/main.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/os.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/parameters.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/paths.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/processed_target.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/processing.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/processing_like.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/publish/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/publish/credentials.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/recipes/README.md +0 -0
- {starbash-0.3.0/src/starbash/templates → starbash-0.3.1/src/starbash/recipes}/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/report.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/rich.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/safety.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/score.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/selection.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/sim_siril/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/sim_siril/connection.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/sim_siril/enums.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/sim_siril/utility.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/siril/import_registration.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/stage_utils.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/stages.py +0 -0
- {starbash-0.3.0/src/starbash/templates/report → starbash-0.3.1/src/starbash/templates}/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/templates/repo/master.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/templates/repo/processed.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/templates/report/README.md.jinja +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/templates/report/index.md.jinja +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/templates/target/master.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/templates/target/processed/about.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/templates/target/processed/main.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/templates/target/processed/sessions.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/templates/userconfig.toml +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/toml.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/tool/__init__.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/tool/base.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/tool/context.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/tool/graxpert.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/tool/python.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/tool/rcastro.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/tool/siril.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/ui/main.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/url.py +0 -0
- {starbash-0.3.0 → starbash-0.3.1}/src/starbash/windows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: starbash
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: A tool for automating/standardizing/sharing astrophotography workflows.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Author: Kevin Hester
|
|
@@ -43,12 +43,14 @@ A tool for automating/standardizing/sharing astrophotography workflows.
|
|
|
43
43
|
## What is starbash?
|
|
44
44
|
<img src="https://raw.githubusercontent.com/geeksville/starbash/refs/heads/main/doc/icon/generated/web/icon-192.png" alt="Starbash: Astrophotography workflows simplified" width="30%" align="right" style="margin-bottom: 20px;">
|
|
45
45
|
|
|
46
|
-
* Automatic - with sensible defaults that you can change as needed. Just a few keystrokes to
|
|
47
|
-
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work'
|
|
46
|
+
* Automatic - with sensible defaults that you can change as needed. Just a few keystrokes to auto-process an **entire** repo of raw images. It uses sensible customizable defaults - you can optionally tweak parameters and reprocess as needed.
|
|
47
|
+
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work' default processing automated.
|
|
48
48
|
* Fast<sup>1</sup> - even with large image repositories. Automatic master bias and flat generation. Automatic preprocessing/ stacking/background-elimination with reasonable defaults.
|
|
49
|
-
* Multi-session - by default. So your workflows can stack from multiple nights (and still use the correct flats etc...).
|
|
49
|
+
* Multi-session - by default. So your workflows can stack from multiple nights (and still use/auto-generate the correct flats, darks/biases etc...).
|
|
50
50
|
* Shareable - you can share/use recipes for image preprocessing flows. The eventual goal is a transition away from opaque burried scripts. Recipes can come from multiple repos and you or other orgs can host recipes.
|
|
51
|
+
* Automatic (optional) posting/creation of your generated images and workflows to a free ['github pages'](https://docs.github.com/en/pages) website. [Sample](https://geeksville.github.io/starbash-public/targets/sh2-91/).
|
|
51
52
|
* Attribution (by default) - a full set of contributions to final image can be tracked: instrument, raw images, processed-by, recipes-by/version etc...
|
|
53
|
+
* Full session/FITS aware history - select targets by name, date-range, instrument etc...
|
|
52
54
|
|
|
53
55
|
(This project is currently 'alpha' and missing recipes for some workflows, but adding new recipes is easy and we're happy to help. Please file a GitHub issue if your images are not auto-processed and we'll work out a fix.)
|
|
54
56
|
|
|
@@ -58,7 +60,7 @@ A tool for automating/standardizing/sharing astrophotography workflows.
|
|
|
58
60
|
## What is starbash NOT
|
|
59
61
|
|
|
60
62
|
### Not a new image processing tool (like Siril)
|
|
61
|
-
It is important to understand that Starbash is primarily a tool for **repeatable**, **sharable**, **semi-automated** workflows. It is tool agnostic and really just focuses on the relationship between tools, images and sessions. It builds upon the great work of existing tools (currently mainly Siril and
|
|
63
|
+
It is important to understand that Starbash is primarily a tool for **repeatable**, **sharable**, **semi-automated** workflows. It is tool agnostic and really just focuses on the relationship between tools, images and sessions. It builds upon the great work of existing tools (currently mainly [Siril](https://siril.org/), [GraXpert](https://graxpert.com/), [Blurxterminator](https://www.rc-astro.com/software/bxt/) and [Starnet2](https://starnetastro.com/cli-tools/)) so we can grow an ecosystem of recipes.
|
|
62
64
|
|
|
63
65
|
### Not a set of scripts
|
|
64
66
|
|
|
@@ -98,7 +100,12 @@ If you are interested in alpha-testing we ❤️ you. This README should have e
|
|
|
98
100
|
|
|
99
101
|
### From the alpha 3 (0.3.0) release (2026/08/31)
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
- Added end-to-end GitHub Pages publishing, including a friendlier GitHub setup flow and automatic token refresh.
|
|
104
|
+
- Introduced the new processing layout with shareable per-target configuration and improved recipe/dependency handling.
|
|
105
|
+
- Added Starnet, BlurXTerminator, and NoiseXTerminator support, with live Siril progress and automatic star recomposition.
|
|
106
|
+
- Improved FITS/session reporting with focal-length and FWHM information, plus more reliable OSC stacking.
|
|
107
|
+
- Added safer cleanup of temporary processing files and clearer warnings when external tools are missing.
|
|
108
|
+
- Publishing is now about twice as fast thanks to concurrent uploads.
|
|
102
109
|
|
|
103
110
|
## Features coming soon
|
|
104
111
|
|
|
@@ -9,12 +9,14 @@ A tool for automating/standardizing/sharing astrophotography workflows.
|
|
|
9
9
|
## What is starbash?
|
|
10
10
|
<img src="https://raw.githubusercontent.com/geeksville/starbash/refs/heads/main/doc/icon/generated/web/icon-192.png" alt="Starbash: Astrophotography workflows simplified" width="30%" align="right" style="margin-bottom: 20px;">
|
|
11
11
|
|
|
12
|
-
* Automatic - with sensible defaults that you can change as needed. Just a few keystrokes to
|
|
13
|
-
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work'
|
|
12
|
+
* Automatic - with sensible defaults that you can change as needed. Just a few keystrokes to auto-process an **entire** repo of raw images. It uses sensible customizable defaults - you can optionally tweak parameters and reprocess as needed.
|
|
13
|
+
* Easy - provides a 'seestar-like' autoprocessing all your sessions (by default). You can optionally do manual postprocessing with all this 'grunt work' default processing automated.
|
|
14
14
|
* Fast<sup>1</sup> - even with large image repositories. Automatic master bias and flat generation. Automatic preprocessing/ stacking/background-elimination with reasonable defaults.
|
|
15
|
-
* Multi-session - by default. So your workflows can stack from multiple nights (and still use the correct flats etc...).
|
|
15
|
+
* Multi-session - by default. So your workflows can stack from multiple nights (and still use/auto-generate the correct flats, darks/biases etc...).
|
|
16
16
|
* Shareable - you can share/use recipes for image preprocessing flows. The eventual goal is a transition away from opaque burried scripts. Recipes can come from multiple repos and you or other orgs can host recipes.
|
|
17
|
+
* Automatic (optional) posting/creation of your generated images and workflows to a free ['github pages'](https://docs.github.com/en/pages) website. [Sample](https://geeksville.github.io/starbash-public/targets/sh2-91/).
|
|
17
18
|
* Attribution (by default) - a full set of contributions to final image can be tracked: instrument, raw images, processed-by, recipes-by/version etc...
|
|
19
|
+
* Full session/FITS aware history - select targets by name, date-range, instrument etc...
|
|
18
20
|
|
|
19
21
|
(This project is currently 'alpha' and missing recipes for some workflows, but adding new recipes is easy and we're happy to help. Please file a GitHub issue if your images are not auto-processed and we'll work out a fix.)
|
|
20
22
|
|
|
@@ -24,7 +26,7 @@ A tool for automating/standardizing/sharing astrophotography workflows.
|
|
|
24
26
|
## What is starbash NOT
|
|
25
27
|
|
|
26
28
|
### Not a new image processing tool (like Siril)
|
|
27
|
-
It is important to understand that Starbash is primarily a tool for **repeatable**, **sharable**, **semi-automated** workflows. It is tool agnostic and really just focuses on the relationship between tools, images and sessions. It builds upon the great work of existing tools (currently mainly Siril and
|
|
29
|
+
It is important to understand that Starbash is primarily a tool for **repeatable**, **sharable**, **semi-automated** workflows. It is tool agnostic and really just focuses on the relationship between tools, images and sessions. It builds upon the great work of existing tools (currently mainly [Siril](https://siril.org/), [GraXpert](https://graxpert.com/), [Blurxterminator](https://www.rc-astro.com/software/bxt/) and [Starnet2](https://starnetastro.com/cli-tools/)) so we can grow an ecosystem of recipes.
|
|
28
30
|
|
|
29
31
|
### Not a set of scripts
|
|
30
32
|
|
|
@@ -64,7 +66,12 @@ If you are interested in alpha-testing we ❤️ you. This README should have e
|
|
|
64
66
|
|
|
65
67
|
### From the alpha 3 (0.3.0) release (2026/08/31)
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
- Added end-to-end GitHub Pages publishing, including a friendlier GitHub setup flow and automatic token refresh.
|
|
70
|
+
- Introduced the new processing layout with shareable per-target configuration and improved recipe/dependency handling.
|
|
71
|
+
- Added Starnet, BlurXTerminator, and NoiseXTerminator support, with live Siril progress and automatic star recomposition.
|
|
72
|
+
- Improved FITS/session reporting with focal-length and FWHM information, plus more reliable OSC stacking.
|
|
73
|
+
- Added safer cleanup of temporary processing files and clearer warnings when external tools are missing.
|
|
74
|
+
- Publishing is now about twice as fast thanks to concurrent uploads.
|
|
68
75
|
|
|
69
76
|
## Features coming soon
|
|
70
77
|
|
|
Binary file
|
|
@@ -27,10 +27,10 @@ UPLOAD_PATH_BLACKLIST = ("Gemfile",)
|
|
|
27
27
|
MAX_BLOB_UPLOADS = 4
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def _rewrite() -> Path:
|
|
30
|
+
def _rewrite(github_username: str | None = None) -> Path:
|
|
31
31
|
site: Path | None = None
|
|
32
32
|
with Starbash("publish.github.rewrite") as sb:
|
|
33
|
-
site = GitHubPublisher(sb).publish()
|
|
33
|
+
site = GitHubPublisher(sb, github_username=github_username).publish()
|
|
34
34
|
assert site is not None
|
|
35
35
|
console.print(f"Generated site: {site}")
|
|
36
36
|
return site
|
|
@@ -248,7 +248,18 @@ def _publish_github(dry_run: bool, login: bool) -> None:
|
|
|
248
248
|
if credential is None:
|
|
249
249
|
credential = _authenticate()
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
service: GitHubService | None = None
|
|
252
|
+
owner: str | None = None
|
|
253
|
+
if not dry_run:
|
|
254
|
+
assert credential is not None
|
|
255
|
+
service = _credential_service(credential)
|
|
256
|
+
if credential.needs_refresh() and credential.refresh_token:
|
|
257
|
+
service.apply_token_response(
|
|
258
|
+
service.refresh_access_token(CLIENT_ID, credential.refresh_token)
|
|
259
|
+
)
|
|
260
|
+
owner = str(service.user()["login"])
|
|
261
|
+
|
|
262
|
+
site = _rewrite(owner) if owner else _rewrite()
|
|
252
263
|
files = sorted(
|
|
253
264
|
path
|
|
254
265
|
for path in site.rglob("*")
|
|
@@ -271,13 +282,10 @@ def _publish_github(dry_run: bool, login: bool) -> None:
|
|
|
271
282
|
console.print("No GitHub repository, branch, commit, or Pages configuration was changed.")
|
|
272
283
|
return
|
|
273
284
|
assert credential is not None
|
|
285
|
+
assert service is not None
|
|
286
|
+
assert owner is not None
|
|
274
287
|
with analytics_start_span(name="github", op="upload"):
|
|
275
|
-
service = _credential_service(credential)
|
|
276
288
|
try:
|
|
277
|
-
if credential.needs_refresh() and credential.refresh_token:
|
|
278
|
-
service.apply_token_response(
|
|
279
|
-
service.refresh_access_token(CLIENT_ID, credential.refresh_token)
|
|
280
|
-
)
|
|
281
289
|
with Progress(
|
|
282
290
|
SpinnerColumn(),
|
|
283
291
|
TextColumn("[progress.description]{task.description}"),
|
|
@@ -288,7 +296,6 @@ def _publish_github(dry_run: bool, login: bool) -> None:
|
|
|
288
296
|
) as progress:
|
|
289
297
|
operation = progress.add_task("Contacting GitHub", total=8 + len(files))
|
|
290
298
|
|
|
291
|
-
owner = str(service.user()["login"])
|
|
292
299
|
pages_url = f"https://{owner}.github.io/starbash-public/"
|
|
293
300
|
progress.update(operation, description=f"Authenticated as {owner}", advance=1)
|
|
294
301
|
_require_app_installation(service)
|
|
@@ -64,9 +64,15 @@ def equipment_rows(equipment: Any) -> list[dict[str, str | None]]:
|
|
|
64
64
|
class GitHubPublisher:
|
|
65
65
|
"""Generate a complete local Jekyll site from one processed repository."""
|
|
66
66
|
|
|
67
|
-
def __init__(
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
sb: Any,
|
|
70
|
+
site_dir: Path | None = None,
|
|
71
|
+
github_username: str | None = None,
|
|
72
|
+
) -> None:
|
|
68
73
|
self.sb = sb
|
|
69
74
|
self.site_dir = site_dir or get_publish_site_dir()
|
|
75
|
+
self.github_username = github_username
|
|
70
76
|
self.environment = Environment(loader=PackageLoader("starbash", "templates/report"))
|
|
71
77
|
|
|
72
78
|
def _processed_root(self) -> Path:
|
|
@@ -133,17 +139,21 @@ class GitHubPublisher:
|
|
|
133
139
|
assets_root = self.site_dir / "assets" / "targets"
|
|
134
140
|
posts.mkdir(parents=True, exist_ok=True)
|
|
135
141
|
assets_root.mkdir(parents=True, exist_ok=True)
|
|
142
|
+
config = resources.files("starbash.templates.report").joinpath("_config.yml")
|
|
136
143
|
(self.site_dir / "_config.yml").write_text(
|
|
137
|
-
"
|
|
144
|
+
config.read_text(encoding="utf-8"), encoding="utf-8"
|
|
138
145
|
)
|
|
146
|
+
gemfile = resources.files("starbash.templates.report").joinpath("Gemfile")
|
|
139
147
|
(self.site_dir / "Gemfile").write_text(
|
|
140
|
-
|
|
141
|
-
'gem "github-pages", group: :jekyll_plugins\n'
|
|
148
|
+
gemfile.read_text(encoding="utf-8"), encoding="utf-8"
|
|
142
149
|
)
|
|
143
150
|
layouts = self.site_dir / "_layouts"
|
|
144
151
|
layouts.mkdir(exist_ok=True)
|
|
145
152
|
default_layout = resources.files("starbash.templates.report").joinpath("default.html")
|
|
146
153
|
(layouts / "default.html").write_text(default_layout.read_text(encoding="utf-8"), encoding="utf-8")
|
|
154
|
+
favicon = resources.files("starbash.assets").joinpath("favicon.ico")
|
|
155
|
+
with favicon.open("rb") as source, (self.site_dir / "favicon.ico").open("wb") as destination:
|
|
156
|
+
shutil.copyfileobj(source, destination)
|
|
147
157
|
index_targets: list[dict[str, Any]] = []
|
|
148
158
|
for directory, document in self._targets(root):
|
|
149
159
|
about = document.get("about", {})
|
|
@@ -158,6 +168,11 @@ class GitHubPublisher:
|
|
|
158
168
|
if isinstance(summary, str):
|
|
159
169
|
about = {**about, "summary": summary}
|
|
160
170
|
name = str(target.get("id") or directory.name)
|
|
171
|
+
description = about.get("description")
|
|
172
|
+
if not isinstance(description, str) or not description.strip():
|
|
173
|
+
description = about.get("summary")
|
|
174
|
+
if not isinstance(description, str) or not description.strip():
|
|
175
|
+
description = f"Processed Starbash target: {name}."
|
|
161
176
|
slug = slugify(name)
|
|
162
177
|
asset_dir = assets_root / slug
|
|
163
178
|
asset_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -191,11 +206,16 @@ class GitHubPublisher:
|
|
|
191
206
|
"chart": f"../../assets/targets/{slug}/{chart_name}",
|
|
192
207
|
}
|
|
193
208
|
)
|
|
209
|
+
page_images = [f"../../{url}" for url in image_urls]
|
|
210
|
+
seo_image = f"/{image_urls[0]}" if image_urls else None
|
|
194
211
|
page_name = f"{slug}.md"
|
|
195
212
|
post = self.environment.get_template("target.md.jinja").render(
|
|
196
213
|
target={**target, "name": name},
|
|
197
214
|
about=about,
|
|
198
|
-
|
|
215
|
+
description=description,
|
|
216
|
+
github_username=self.github_username,
|
|
217
|
+
images=page_images,
|
|
218
|
+
image=seo_image,
|
|
199
219
|
sessions=sessions,
|
|
200
220
|
workflow_url=f"../../assets/targets/{slug}/main.toml",
|
|
201
221
|
)
|
|
@@ -28,6 +28,13 @@ class GitHubAuthenticationError(GitHubError):
|
|
|
28
28
|
"""The saved access token was rejected by GitHub."""
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
class GitHubTimeoutError(GitHubError):
|
|
32
|
+
"""A GitHub request timed out."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
GITHUB_REQUEST_ATTEMPTS = 3
|
|
36
|
+
|
|
37
|
+
|
|
31
38
|
@dataclass(frozen=True)
|
|
32
39
|
class DeviceCode:
|
|
33
40
|
device_code: str
|
|
@@ -96,6 +103,37 @@ class GitHubService:
|
|
|
96
103
|
url: str,
|
|
97
104
|
payload: dict[str, Any] | None = None,
|
|
98
105
|
_retried_after_refresh: bool = False,
|
|
106
|
+
) -> dict[str, Any]:
|
|
107
|
+
"""Make a GitHub request, retrying transient timeouts."""
|
|
108
|
+
for attempt in range(1, GITHUB_REQUEST_ATTEMPTS + 1):
|
|
109
|
+
try:
|
|
110
|
+
return self._request_once(
|
|
111
|
+
method, url, payload, _retried_after_refresh
|
|
112
|
+
)
|
|
113
|
+
except GitHubTimeoutError:
|
|
114
|
+
if attempt == GITHUB_REQUEST_ATTEMPTS:
|
|
115
|
+
logger.warning(
|
|
116
|
+
"GitHub request timed out after %d attempts: %s %s",
|
|
117
|
+
GITHUB_REQUEST_ATTEMPTS,
|
|
118
|
+
method,
|
|
119
|
+
url,
|
|
120
|
+
)
|
|
121
|
+
raise
|
|
122
|
+
logger.warning(
|
|
123
|
+
"GitHub request timed out; retrying (attempt %d/%d): %s %s",
|
|
124
|
+
attempt + 1,
|
|
125
|
+
GITHUB_REQUEST_ATTEMPTS,
|
|
126
|
+
method,
|
|
127
|
+
url,
|
|
128
|
+
)
|
|
129
|
+
raise AssertionError("unreachable")
|
|
130
|
+
|
|
131
|
+
def _request_once(
|
|
132
|
+
self,
|
|
133
|
+
method: str,
|
|
134
|
+
url: str,
|
|
135
|
+
payload: dict[str, Any] | None = None,
|
|
136
|
+
_retried_after_refresh: bool = False,
|
|
99
137
|
) -> dict[str, Any]:
|
|
100
138
|
headers = {
|
|
101
139
|
"Accept": "application/vnd.github+json",
|
|
@@ -122,8 +160,10 @@ class GitHubService:
|
|
|
122
160
|
return result
|
|
123
161
|
except urllib.error.HTTPError as exc:
|
|
124
162
|
response_body = exc.read().decode("utf-8", errors="replace")
|
|
163
|
+
parsed_response: Any = None
|
|
125
164
|
try:
|
|
126
|
-
|
|
165
|
+
parsed_response = json.loads(response_body)
|
|
166
|
+
response_body = repr(self._safe_response(parsed_response))
|
|
127
167
|
except json.JSONDecodeError:
|
|
128
168
|
response_body = response_body[:1000]
|
|
129
169
|
logger.debug(
|
|
@@ -133,6 +173,10 @@ class GitHubService:
|
|
|
133
173
|
exc.code,
|
|
134
174
|
response_body,
|
|
135
175
|
)
|
|
176
|
+
if exc.code == 422 and isinstance(parsed_response, dict):
|
|
177
|
+
message = parsed_response.get("message")
|
|
178
|
+
if isinstance(message, str) and "timed out" in message.lower():
|
|
179
|
+
raise GitHubTimeoutError("GitHub request timed out") from exc
|
|
136
180
|
if exc.code == 404:
|
|
137
181
|
raise GitHubError("GitHub resource was not found") from exc
|
|
138
182
|
if exc.code == 401:
|
|
@@ -158,6 +202,11 @@ class GitHubService:
|
|
|
158
202
|
raise GitHubError(f"GitHub API request failed ({exc.code})") from exc
|
|
159
203
|
except (urllib.error.URLError, TimeoutError) as exc:
|
|
160
204
|
logger.debug("GitHub request failed: %s %s error=%r", method, url, exc)
|
|
205
|
+
if isinstance(exc, TimeoutError) or (
|
|
206
|
+
isinstance(exc, urllib.error.URLError)
|
|
207
|
+
and isinstance(exc.reason, TimeoutError)
|
|
208
|
+
):
|
|
209
|
+
raise GitHubTimeoutError("GitHub request timed out") from exc
|
|
161
210
|
raise GitHubError("Could not connect to GitHub") from exc
|
|
162
211
|
|
|
163
212
|
def device_code(self, client_id: str, scope: str = "repo offline_access") -> DeviceCode:
|
|
@@ -349,8 +398,11 @@ class GitHubService:
|
|
|
349
398
|
)
|
|
350
399
|
|
|
351
400
|
def create_blob(self, owner: str, name: str, content: bytes) -> str:
|
|
401
|
+
"""Create a Git blob."""
|
|
352
402
|
encoded = base64.b64encode(content).decode("ascii")
|
|
353
|
-
|
|
403
|
+
url = f"{self.api}/repos/{owner}/{name}/git/blobs"
|
|
404
|
+
payload = {"content": encoded, "encoding": "base64"}
|
|
405
|
+
return str(self._request("POST", url, payload)["sha"])
|
|
354
406
|
|
|
355
407
|
def create_tree(self, owner: str, name: str, entries: list[dict[str, str]]) -> str:
|
|
356
408
|
return str(self._request("POST", f"{self.api}/repos/{owner}/{name}/git/trees", {"tree": entries})["sha"])
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Reusable crop and rotation helpers for processing recipes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import math
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from starbash.fits import read_dimensions
|
|
11
|
+
from starbash.tool import tools
|
|
12
|
+
|
|
13
|
+
siril = tools["siril"]
|
|
14
|
+
|
|
15
|
+
# These globals are populated by recipe scripts before calling ``crop_files``.
|
|
16
|
+
context: dict[str, Any] = {}
|
|
17
|
+
logger: logging.Logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def crop_rectangle(width: int, height: int, crop_percent: int = 90) -> tuple[int, int, int, int]:
|
|
21
|
+
"""Return a centered crop rectangle retaining ``crop_percent`` of an image."""
|
|
22
|
+
if width <= 0 or height <= 0:
|
|
23
|
+
raise ValueError(f"Image dimensions must be positive, got {width}x{height}")
|
|
24
|
+
if isinstance(crop_percent, bool) or not isinstance(crop_percent, int):
|
|
25
|
+
raise ValueError(f"crop_percent must be an integer, got {crop_percent!r}")
|
|
26
|
+
if not 1 <= crop_percent <= 100:
|
|
27
|
+
raise ValueError(f"crop_percent must be between 1 and 100, got {crop_percent}")
|
|
28
|
+
|
|
29
|
+
crop_width = width * crop_percent // 100
|
|
30
|
+
crop_height = height * crop_percent // 100
|
|
31
|
+
if crop_width < 1 or crop_height < 1:
|
|
32
|
+
raise ValueError(
|
|
33
|
+
f"crop_percent={crop_percent} produces an empty crop for {width}x{height}"
|
|
34
|
+
)
|
|
35
|
+
crop_x = (width - crop_width) // 2
|
|
36
|
+
crop_y = (height - crop_height) // 2
|
|
37
|
+
return crop_x, crop_y, crop_width, crop_height
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def crop_files(
|
|
41
|
+
input_paths: list[Path],
|
|
42
|
+
output_paths: list[Path],
|
|
43
|
+
crop_percent: int = 90,
|
|
44
|
+
rotate_deg: int | float = 0,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Crop and rotate each input FITS file into its corresponding output."""
|
|
47
|
+
if not input_paths or not output_paths:
|
|
48
|
+
raise ValueError("crop_files requires at least one input and output path")
|
|
49
|
+
if len(input_paths) != len(output_paths):
|
|
50
|
+
raise ValueError("crop_files requires equal numbers of input and output paths")
|
|
51
|
+
if isinstance(rotate_deg, bool) or not isinstance(rotate_deg, (int, float)):
|
|
52
|
+
raise ValueError(f"rotate_deg must be numeric, got {rotate_deg!r}")
|
|
53
|
+
if not math.isfinite(rotate_deg):
|
|
54
|
+
raise ValueError(f"rotate_deg must be finite, got {rotate_deg!r}")
|
|
55
|
+
|
|
56
|
+
commands: list[str] = []
|
|
57
|
+
for input_path, output_path in zip(input_paths, output_paths, strict=True):
|
|
58
|
+
width, height = read_dimensions(input_path)
|
|
59
|
+
crop_x, crop_y, crop_width, crop_height = crop_rectangle(
|
|
60
|
+
width, height, crop_percent
|
|
61
|
+
)
|
|
62
|
+
commands.extend(
|
|
63
|
+
[
|
|
64
|
+
f'load "{input_path}"',
|
|
65
|
+
f"crop {crop_x} {crop_y} {crop_width} {crop_height}",
|
|
66
|
+
]
|
|
67
|
+
)
|
|
68
|
+
if rotate_deg != 0:
|
|
69
|
+
commands.append(f"rotate {rotate_deg:g} -interp=lanczos4")
|
|
70
|
+
commands.append(f'save "{output_path}"')
|
|
71
|
+
|
|
72
|
+
process_dir = context.get("process_dir")
|
|
73
|
+
if not process_dir:
|
|
74
|
+
raise ValueError("crop_files requires context['process_dir']")
|
|
75
|
+
siril.run("\n".join(commands), context=context, cwd=process_dir)
|
|
@@ -7,7 +7,6 @@ from pathlib import Path
|
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
9
|
from starbash.exception import NotEnoughFilesError
|
|
10
|
-
from starbash.fits import read_dimensions
|
|
11
10
|
from starbash.siril.import_registration import parse_siril_conversion, parse_siril_seq
|
|
12
11
|
from starbash.tool import tools
|
|
13
12
|
|
|
@@ -83,36 +82,6 @@ def fix_sequence_name(path: str) -> str:
|
|
|
83
82
|
return base
|
|
84
83
|
|
|
85
84
|
|
|
86
|
-
def _crop_rectangle(width: int, height: int, crop_percent: int = 90) -> tuple[int, int, int, int]:
|
|
87
|
-
"""Calculate a centered crop rectangle for an image."""
|
|
88
|
-
if width <= 0 or height <= 0:
|
|
89
|
-
raise ValueError(f"Image dimensions must be positive, got {width}x{height}")
|
|
90
|
-
if not 0 < crop_percent <= 100:
|
|
91
|
-
raise ValueError(f"crop_percent must be between 1 and 100, got {crop_percent}")
|
|
92
|
-
|
|
93
|
-
crop_width = width * crop_percent // 100
|
|
94
|
-
crop_height = height * crop_percent // 100
|
|
95
|
-
crop_x = (width - crop_width) // 2
|
|
96
|
-
crop_y = (height - crop_height) // 2
|
|
97
|
-
return crop_x, crop_y, crop_width, crop_height
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def _crop_final_files(final_paths: list[Path]) -> None:
|
|
101
|
-
"""Crop final OSC outputs in place using Siril's image crop command."""
|
|
102
|
-
commands = ""
|
|
103
|
-
for final_path in final_paths:
|
|
104
|
-
width, height = read_dimensions(final_path)
|
|
105
|
-
crop_x, crop_y, crop_width, crop_height = _crop_rectangle(width, height)
|
|
106
|
-
commands += f"""
|
|
107
|
-
load "{final_path}"
|
|
108
|
-
crop {crop_x} {crop_y} {crop_width} {crop_height}
|
|
109
|
-
save "{final_path}"
|
|
110
|
-
"""
|
|
111
|
-
|
|
112
|
-
if commands:
|
|
113
|
-
siril.run(commands, context=context, cwd=context["process_dir"])
|
|
114
|
-
|
|
115
|
-
|
|
116
85
|
def make_stacked(inputs_to_use: list[Any], variant: str | None, output_file: str) -> None:
|
|
117
86
|
"""
|
|
118
87
|
Registers and stacks all pre-processed light frames for a given filter configuration
|
|
@@ -210,12 +179,9 @@ def make_renormalize(channel_num: int) -> None:
|
|
|
210
179
|
os.makedirs(results_dir, exist_ok=True)
|
|
211
180
|
|
|
212
181
|
commands = ""
|
|
213
|
-
final_paths: list[Path] = []
|
|
214
|
-
|
|
215
182
|
if channel_num == 1:
|
|
216
183
|
# Only one channel - just copy it - eventually we'll add other metadata
|
|
217
184
|
final_path = f"{results_dir}/stacked.fits"
|
|
218
|
-
final_paths.append(Path(final_path))
|
|
219
185
|
commands += f"""
|
|
220
186
|
load results_00001
|
|
221
187
|
save "{final_path}"
|
|
@@ -231,7 +197,6 @@ def make_renormalize(channel_num: int) -> None:
|
|
|
231
197
|
|
|
232
198
|
ha_final_path = f"{results_dir}/stacked_Ha.fits"
|
|
233
199
|
oiii_final_path = f"{results_dir}/stacked_OIII.fits"
|
|
234
|
-
final_paths.extend([Path(ha_final_path), Path(oiii_final_path)])
|
|
235
200
|
|
|
236
201
|
# Pixel math formula for renormalization.
|
|
237
202
|
# It matches the median and spread (MAD) of a channel to a reference channel (Ha).
|
|
@@ -258,7 +223,6 @@ def make_renormalize(channel_num: int) -> None:
|
|
|
258
223
|
logger.info("Doing renormalisation of extra Sii channel")
|
|
259
224
|
|
|
260
225
|
sii_final_path = f"{results_dir}/stacked_Sii.fits"
|
|
261
|
-
final_paths.append(Path(sii_final_path))
|
|
262
226
|
r_sii = f"r_{sii_base}"
|
|
263
227
|
pm_sii = f'"${r_sii}$*mad(${r_ha}$)/mad(${r_sii}$)-mad(${r_ha}$)/mad(${r_sii}$)*median(${r_sii}$)+median(${r_ha}$)"'
|
|
264
228
|
commands += f"""
|
|
@@ -268,7 +232,6 @@ def make_renormalize(channel_num: int) -> None:
|
|
|
268
232
|
"""
|
|
269
233
|
|
|
270
234
|
siril.run(commands, context=context, cwd=context["process_dir"])
|
|
271
|
-
_crop_final_files(final_paths)
|
|
272
235
|
logger.info(f"Saved final renormalized images to {results_dir}")
|
|
273
236
|
|
|
274
237
|
|
|
File without changes
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="utf-8">
|
|
6
6
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
7
|
+
<link rel="icon" href="{{ '/favicon.ico' | relative_url }}" type="image/x-icon">
|
|
7
8
|
|
|
8
9
|
{% seo %}
|
|
9
10
|
<link rel="stylesheet" href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}">
|
|
@@ -31,7 +32,7 @@
|
|
|
31
32
|
<span style="display: block; flex: 1 1 50%; min-width: 200px;"><a
|
|
32
33
|
href="https://github.com/geeksville/starbash">Starbash</a>
|
|
33
34
|
is a young project, and we would love to
|
|
34
|
-
improve it with your help. Please try it, and we can work together
|
|
35
|
+
improve it with your help. Please try it, and we can work together via github issues.
|
|
35
36
|
</span>
|
|
36
37
|
</div>
|
|
37
38
|
</div><!-- end header -->
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
layout: default
|
|
3
|
-
title:
|
|
3
|
+
title: {{ (target.name ~ ' by ' ~ (github_username | default('Starbash', true))) | tojson }}
|
|
4
|
+
description: {{ description | default('Processed Starbash target.', true) | tojson }}
|
|
5
|
+
{% if image is defined and image %}image: {{ image | tojson }}{% endif %}
|
|
4
6
|
---
|
|
5
7
|
|
|
6
8
|
# {{ target.name }}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
1
|
import configparser
|
|
4
2
|
import logging
|
|
3
|
+
import shutil
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
|
|
7
6
|
from platformdirs import PlatformDirs
|
|
@@ -25,10 +24,11 @@ class StarnetTool(SirilTool):
|
|
|
25
24
|
return Path(PlatformDirs("siril").user_config_dir)
|
|
26
25
|
|
|
27
26
|
def _starnet_configured(self) -> bool:
|
|
28
|
-
"""
|
|
27
|
+
"""Ensure Siril has a usable ``starnet_exe`` and report whether it is configured."""
|
|
29
28
|
config_dir = self._siril_config_dir()
|
|
30
29
|
# Siril versions its config file (e.g. config.1.4.ini); check whichever exist.
|
|
31
|
-
|
|
30
|
+
ini_paths = sorted(config_dir.glob("config.*.ini"))
|
|
31
|
+
for ini_path in ini_paths:
|
|
32
32
|
parser = configparser.ConfigParser()
|
|
33
33
|
try:
|
|
34
34
|
parser.read(ini_path)
|
|
@@ -36,6 +36,27 @@ class StarnetTool(SirilTool):
|
|
|
36
36
|
continue
|
|
37
37
|
if parser.get("core", "starnet_exe", fallback="").strip():
|
|
38
38
|
return True
|
|
39
|
+
|
|
40
|
+
starnet_path = shutil.which("starnet2")
|
|
41
|
+
if starnet_path and ini_paths:
|
|
42
|
+
ini_path = ini_paths[-1]
|
|
43
|
+
parser = configparser.ConfigParser()
|
|
44
|
+
try:
|
|
45
|
+
parser.read(ini_path)
|
|
46
|
+
if not parser.has_section("core"):
|
|
47
|
+
parser.add_section("core")
|
|
48
|
+
parser.set("core", "starnet_exe", str(Path(starnet_path).resolve()))
|
|
49
|
+
with ini_path.open("w", encoding="utf-8") as config_file:
|
|
50
|
+
parser.write(config_file)
|
|
51
|
+
except (OSError, configparser.Error) as exc:
|
|
52
|
+
logger.warning("Unable to add starnet2 to Siril config %s: %s", ini_path, exc)
|
|
53
|
+
else:
|
|
54
|
+
logger.warning(
|
|
55
|
+
"Added starnet2 at %s to the Siril config file %s",
|
|
56
|
+
Path(starnet_path).resolve(),
|
|
57
|
+
ini_path,
|
|
58
|
+
)
|
|
59
|
+
return True
|
|
39
60
|
return False
|
|
40
61
|
|
|
41
62
|
@property
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{starbash-0.3.0/src/starbash/templates/report → starbash-0.3.1/src/starbash/templates}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|