gh-space-shooter 0.0.4__tar.gz → 1.0.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.
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/.github/workflows/publish.yml +6 -5
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/.github/workflows/test.yml +2 -2
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/.gitignore +2 -1
- gh_space_shooter-1.0.0/.python-version +1 -0
- gh_space_shooter-1.0.0/CLAUDE.md +90 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/PKG-INFO +11 -6
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/README.md +9 -4
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/action.yml +1 -1
- gh_space_shooter-1.0.0/app/README.md +53 -0
- gh_space_shooter-1.0.0/app/public/favicon.ico +0 -0
- gh_space_shooter-1.0.0/app/pyproject.toml +19 -0
- gh_space_shooter-1.0.0/app/src/main.py +78 -0
- gh_space_shooter-1.0.0/app/src/templates/index.html +507 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/pyproject.toml +9 -3
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/cli.py +25 -4
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/animator.py +18 -8
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/renderer.py +31 -5
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/github_client.py +1 -1
- gh_space_shooter-1.0.0/src/gh_space_shooter/py.typed +0 -0
- gh_space_shooter-1.0.0/uv.lock +1093 -0
- gh_space_shooter-0.0.4/.python-version +0 -1
- gh_space_shooter-0.0.4/uv.lock +0 -305
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/.github/dependabot.yml +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/LICENSE +0 -0
- /gh_space_shooter-0.0.4/src/gh_space_shooter/py.typed → /gh_space_shooter-1.0.0/app/src/__init__.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/example.gif +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/__init__.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/console_printer.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/constants.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/__init__.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/__init__.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/bullet.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/drawable.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/enemy.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/explosion.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/ship.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/drawables/starfield.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/game_state.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/render_context.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/__init__.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/base_strategy.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/column_strategy.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/random_strategy.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/src/gh_space_shooter/game/strategies/row_strategy.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/tests/conftest.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/tests/test_bullet_collision.py +0 -0
- {gh_space_shooter-0.0.4 → gh_space_shooter-1.0.0}/tests/test_strategies.py +0 -0
|
@@ -14,7 +14,7 @@ on:
|
|
|
14
14
|
- patch
|
|
15
15
|
|
|
16
16
|
env:
|
|
17
|
-
PYTHON_LATEST: 3.
|
|
17
|
+
PYTHON_LATEST: 3.12
|
|
18
18
|
|
|
19
19
|
jobs:
|
|
20
20
|
build:
|
|
@@ -45,11 +45,12 @@ jobs:
|
|
|
45
45
|
- name: Publish
|
|
46
46
|
run: uv publish --token ${{ secrets.PYPI_API_TOKEN }}
|
|
47
47
|
|
|
48
|
-
- name: GitHub Release v${{ steps.bump.outputs.current-version }}
|
|
48
|
+
- name: GitHub Release PIP v${{ steps.bump.outputs.current-version }}
|
|
49
49
|
uses: ncipollo/release-action@v1
|
|
50
50
|
with:
|
|
51
|
-
name: gh-space-shooter v${{ steps.bump.outputs.current-version }}
|
|
51
|
+
name: gh-space-shooter pypi v${{ steps.bump.outputs.current-version }}
|
|
52
52
|
tag: v${{ steps.bump.outputs.current-version }}
|
|
53
|
-
body: "Automated release of version v${{ steps.bump.outputs.current-version }}"
|
|
53
|
+
body: "Automated release of pypi version v${{ steps.bump.outputs.current-version }}"
|
|
54
54
|
artifacts: dist/*
|
|
55
|
-
token: ${{ secrets.GH_TOKEN }}
|
|
55
|
+
token: ${{ secrets.GH_TOKEN }}
|
|
56
|
+
generateReleaseNotes: true
|
|
@@ -7,7 +7,7 @@ on:
|
|
|
7
7
|
types: [opened, synchronize, reopened]
|
|
8
8
|
|
|
9
9
|
env:
|
|
10
|
-
PYTHON_LATEST: 3.
|
|
10
|
+
PYTHON_LATEST: 3.12
|
|
11
11
|
|
|
12
12
|
jobs:
|
|
13
13
|
test:
|
|
@@ -15,7 +15,7 @@ jobs:
|
|
|
15
15
|
runs-on: ubuntu-latest
|
|
16
16
|
strategy:
|
|
17
17
|
matrix:
|
|
18
|
-
python-version: ["3.
|
|
18
|
+
python-version: ["3.12"]
|
|
19
19
|
|
|
20
20
|
steps:
|
|
21
21
|
- name: Checkout code
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Build and Development Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install dependencies (uses uv package manager)
|
|
9
|
+
uv sync
|
|
10
|
+
|
|
11
|
+
# Install with dev dependencies
|
|
12
|
+
uv sync --extra dev
|
|
13
|
+
|
|
14
|
+
# Run the CLI
|
|
15
|
+
uv run gh-space-shooter <username>
|
|
16
|
+
|
|
17
|
+
# Run tests
|
|
18
|
+
uv run pytest tests/ -v
|
|
19
|
+
|
|
20
|
+
# Run a single test file
|
|
21
|
+
uv run pytest tests/test_strategies.py -v
|
|
22
|
+
|
|
23
|
+
# Run a specific test
|
|
24
|
+
uv run pytest tests/test_strategies.py::test_column_strategy -v
|
|
25
|
+
|
|
26
|
+
# Run the web app (from app/ directory)
|
|
27
|
+
uv run --project gh-space-shooter-app uvicorn main:app
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Environment Setup
|
|
31
|
+
|
|
32
|
+
Requires a GitHub Personal Access Token with `read:user` scope:
|
|
33
|
+
```bash
|
|
34
|
+
export GH_TOKEN=your_token_here
|
|
35
|
+
# Or create a .env file with GH_TOKEN=your_token
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Project Context
|
|
39
|
+
|
|
40
|
+
**Current main usage**: GitHub Action that automatically updates a game GIF in user repositories daily (see `.github/workflows/` for the action definition).
|
|
41
|
+
|
|
42
|
+
**Web App**: A FastAPI-based web application is available in the `app/` directory for on-demand GIF generation. See `app/README.md` for details.
|
|
43
|
+
|
|
44
|
+
## Architecture Overview
|
|
45
|
+
|
|
46
|
+
This is a CLI tool that transforms GitHub contribution graphs into animated space shooter GIFs using Pillow.
|
|
47
|
+
|
|
48
|
+
### Core Flow
|
|
49
|
+
|
|
50
|
+
1. **CLI (`cli.py`)** - Typer-based entry point that orchestrates the pipeline
|
|
51
|
+
2. **GitHubClient (`github_client.py`)** - Fetches contribution data via GitHub GraphQL API, returns typed `ContributionData` dict
|
|
52
|
+
3. **Animator (`game/animator.py`)** - Main game loop that coordinates strategy execution and frame generation
|
|
53
|
+
4. **GameState (`game/game_state.py`)** - Central state container holding ship, enemies, bullets, explosions
|
|
54
|
+
5. **Renderer (`game/renderer.py`)** - Converts GameState to PIL Images each frame
|
|
55
|
+
|
|
56
|
+
### Strategy Pattern
|
|
57
|
+
|
|
58
|
+
Strategies (`game/strategies/`) define how the ship clears enemies:
|
|
59
|
+
- `BaseStrategy` - Abstract base defining `generate_actions(game_state) -> Iterator[Action]`
|
|
60
|
+
- `ColumnStrategy` - Clears enemies column by column (left to right)
|
|
61
|
+
- `RowStrategy` - Clears enemies row by row (top to bottom)
|
|
62
|
+
- `RandomStrategy` - Targets enemies in random order
|
|
63
|
+
|
|
64
|
+
Strategies yield `Action(x, shoot)` objects. The Animator processes these: moving the ship to position `x`, waiting for movement/cooldown to complete, then shooting if `shoot=True`.
|
|
65
|
+
|
|
66
|
+
### Drawable System
|
|
67
|
+
|
|
68
|
+
All game objects inherit from `Drawable` (`game/drawables/drawable.py`):
|
|
69
|
+
- `animate(delta_time)` - Update state (position, cooldowns, particles)
|
|
70
|
+
- `draw(draw, context)` - Render to PIL ImageDraw
|
|
71
|
+
|
|
72
|
+
Drawables: `Ship`, `Enemy`, `Bullet`, `Explosion`, `Starfield`
|
|
73
|
+
|
|
74
|
+
The `RenderContext` (`game/render_context.py`) holds theming (colors, cell sizes, padding) and coordinate conversion helpers.
|
|
75
|
+
|
|
76
|
+
### Animation Loop
|
|
77
|
+
|
|
78
|
+
In `Animator._generate_frames()`:
|
|
79
|
+
1. Strategy yields next action
|
|
80
|
+
2. Ship moves to target x position (animate frames until arrived)
|
|
81
|
+
3. Ship shoots if action.shoot (animate frames for bullet travel + explosions)
|
|
82
|
+
4. Repeat until all enemies destroyed
|
|
83
|
+
|
|
84
|
+
Frame rate is configurable (default 40 FPS). All speeds use delta_time for frame-rate independence.
|
|
85
|
+
|
|
86
|
+
### Key Constants (`constants.py`)
|
|
87
|
+
|
|
88
|
+
- `NUM_WEEKS = 52` - Contribution graph width
|
|
89
|
+
- `NUM_DAYS = 7` - Contribution graph height
|
|
90
|
+
- Speeds are in cells/second, durations in seconds
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gh-space-shooter
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: A CLI tool that visualizes GitHub contribution graphs as gamified GIFs
|
|
5
5
|
Author-email: zane <czl970721@gmail.com>
|
|
6
6
|
License-File: LICENSE
|
|
7
|
-
Requires-Python: >=3.
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
8
|
Requires-Dist: httpx>=0.27.0
|
|
9
9
|
Requires-Dist: pillow>=10.1.0
|
|
10
10
|
Requires-Dist: python-dotenv>=1.0.0
|
|
@@ -22,6 +22,10 @@ Transform your GitHub contribution graph into an epic space shooter game!
|
|
|
22
22
|
|
|
23
23
|
## Usage
|
|
24
24
|
|
|
25
|
+
### Onetime Generation
|
|
26
|
+
|
|
27
|
+
A [web interface](https://gh-space-shooter.kiyo-n-zane.com) is available for on-demand GIF generation without installing anything locally.
|
|
28
|
+
|
|
25
29
|
### GitHub Action
|
|
26
30
|
|
|
27
31
|
Automatically update your game GIF daily using GitHub Actions! Add this workflow to your repository at `.github/workflows/update-game.yml`:
|
|
@@ -122,14 +126,15 @@ gh-space-shooter torvalds --output my-epic-game.gif
|
|
|
122
126
|
gh-space-shooter torvalds -o my-game.gif
|
|
123
127
|
|
|
124
128
|
# Choose enemy attack strategy
|
|
125
|
-
gh-space-shooter torvalds --strategy column # Enemies attack in columns
|
|
126
129
|
gh-space-shooter torvalds --strategy row # Enemies attack in rows
|
|
127
130
|
gh-space-shooter torvalds -s random # Random chaos (default)
|
|
128
131
|
|
|
129
132
|
# Adjust animation frame rate
|
|
130
|
-
gh-space-shooter torvalds --fps 25 #
|
|
131
|
-
gh-space-shooter torvalds --fps 40 # Default
|
|
132
|
-
|
|
133
|
+
gh-space-shooter torvalds --fps 25 # Lower Frame rate, Smaller file size
|
|
134
|
+
gh-space-shooter torvalds --fps 40 # Default Frame rate, Larger file size
|
|
135
|
+
|
|
136
|
+
# Stop the animation earlier
|
|
137
|
+
gh-space-shooter torvalds --max-frame 200 # Stop after 200 frames
|
|
133
138
|
```
|
|
134
139
|
|
|
135
140
|
This creates an animated GIF showing:
|
|
@@ -6,6 +6,10 @@ Transform your GitHub contribution graph into an epic space shooter game!
|
|
|
6
6
|
|
|
7
7
|
## Usage
|
|
8
8
|
|
|
9
|
+
### Onetime Generation
|
|
10
|
+
|
|
11
|
+
A [web interface](https://gh-space-shooter.kiyo-n-zane.com) is available for on-demand GIF generation without installing anything locally.
|
|
12
|
+
|
|
9
13
|
### GitHub Action
|
|
10
14
|
|
|
11
15
|
Automatically update your game GIF daily using GitHub Actions! Add this workflow to your repository at `.github/workflows/update-game.yml`:
|
|
@@ -106,14 +110,15 @@ gh-space-shooter torvalds --output my-epic-game.gif
|
|
|
106
110
|
gh-space-shooter torvalds -o my-game.gif
|
|
107
111
|
|
|
108
112
|
# Choose enemy attack strategy
|
|
109
|
-
gh-space-shooter torvalds --strategy column # Enemies attack in columns
|
|
110
113
|
gh-space-shooter torvalds --strategy row # Enemies attack in rows
|
|
111
114
|
gh-space-shooter torvalds -s random # Random chaos (default)
|
|
112
115
|
|
|
113
116
|
# Adjust animation frame rate
|
|
114
|
-
gh-space-shooter torvalds --fps 25 #
|
|
115
|
-
gh-space-shooter torvalds --fps 40 # Default
|
|
116
|
-
|
|
117
|
+
gh-space-shooter torvalds --fps 25 # Lower Frame rate, Smaller file size
|
|
118
|
+
gh-space-shooter torvalds --fps 40 # Default Frame rate, Larger file size
|
|
119
|
+
|
|
120
|
+
# Stop the animation earlier
|
|
121
|
+
gh-space-shooter torvalds --max-frame 200 # Stop after 200 frames
|
|
117
122
|
```
|
|
118
123
|
|
|
119
124
|
This creates an animated GIF showing:
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# gh-space-shooter Web App
|
|
2
|
+
|
|
3
|
+
A FastAPI web application that provides on-demand GitHub Space Shooter GIF generation through a browser interface.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Web UI for generating GIFs without CLI installation
|
|
8
|
+
- Select GitHub username and animation strategy (random, column, row)
|
|
9
|
+
- Download generated GIFs directly
|
|
10
|
+
- Share functionality for supported browsers
|
|
11
|
+
|
|
12
|
+
## Setup
|
|
13
|
+
|
|
14
|
+
1. Install dependencies:
|
|
15
|
+
```bash
|
|
16
|
+
cd app
|
|
17
|
+
uv sync
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
2. Set up environment:
|
|
21
|
+
```bash
|
|
22
|
+
export GH_TOKEN=your_github_token
|
|
23
|
+
# Or create a .env file with GH_TOKEN=your_token
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Running Locally
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
uv run --project gh-space-shooter-app uvicorn main:app
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The app will be available at `http://localhost:8000`.
|
|
33
|
+
|
|
34
|
+
## API Endpoints
|
|
35
|
+
|
|
36
|
+
- `GET /` - Web UI for generating GIFs
|
|
37
|
+
- `GET /api/generate?username=<username>&strategy=<strategy>` - Generate and return a GIF
|
|
38
|
+
- `username` (required): GitHub username
|
|
39
|
+
- `strategy` (optional): Animation strategy - `random`, `column`, or `row` (default: `random`)
|
|
40
|
+
|
|
41
|
+
## Project Structure
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
app/
|
|
45
|
+
├── src/
|
|
46
|
+
│ ├── main.py # FastAPI application
|
|
47
|
+
│ └── templates/
|
|
48
|
+
│ └── index.html # Web UI template
|
|
49
|
+
├── public/
|
|
50
|
+
│ └── favicon.ico
|
|
51
|
+
├── pyproject.toml
|
|
52
|
+
└── README.md
|
|
53
|
+
```
|
|
Binary file
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gh-space-shooter-app"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Web app wrapper for gh-space-shooter GIF generation"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"gh-space-shooter",
|
|
9
|
+
"fastapi[standard]",
|
|
10
|
+
"uvicorn[standard]",
|
|
11
|
+
"jinja2",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["setuptools"]
|
|
16
|
+
build-backend = "setuptools.build_meta"
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
app = "main:app"
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""FastAPI web app for gh-space-shooter GIF generation."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from io import BytesIO
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from dotenv import load_dotenv
|
|
8
|
+
from fastapi import FastAPI, HTTPException, Query
|
|
9
|
+
from fastapi.requests import Request
|
|
10
|
+
from fastapi.responses import HTMLResponse, Response
|
|
11
|
+
# from fastapi.staticfiles import StaticFiles
|
|
12
|
+
from fastapi.templating import Jinja2Templates
|
|
13
|
+
|
|
14
|
+
from gh_space_shooter.game import Animator, ColumnStrategy, RandomStrategy, RowStrategy, BaseStrategy
|
|
15
|
+
from gh_space_shooter.github_client import GitHubAPIError, GitHubClient
|
|
16
|
+
|
|
17
|
+
load_dotenv()
|
|
18
|
+
|
|
19
|
+
app = FastAPI(title="GitHub Space Shooter")
|
|
20
|
+
|
|
21
|
+
templates = Jinja2Templates(directory=Path(__file__).parent / "templates")
|
|
22
|
+
# app.mount("/public", StaticFiles(directory=Path(__file__).parent / "public"), name="public")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
STRATEGY_MAP: dict[str, type[BaseStrategy]] = {
|
|
26
|
+
"column": ColumnStrategy,
|
|
27
|
+
"row": RowStrategy,
|
|
28
|
+
"random": RandomStrategy,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def generate_gif(username: str, strategy: str, token: str) -> BytesIO:
|
|
33
|
+
"""Generate a space shooter GIF for a GitHub user."""
|
|
34
|
+
with GitHubClient(token) as client:
|
|
35
|
+
data = client.get_contribution_graph(username)
|
|
36
|
+
|
|
37
|
+
strategy_class: type[BaseStrategy] = STRATEGY_MAP.get(strategy, RandomStrategy)
|
|
38
|
+
strat = strategy_class()
|
|
39
|
+
|
|
40
|
+
animator = Animator(data, strat, fps=20, watermark=True)
|
|
41
|
+
return animator.generate_gif(maxFrame=250)
|
|
42
|
+
|
|
43
|
+
@app.get("/", response_class=HTMLResponse)
|
|
44
|
+
async def index(request: Request):
|
|
45
|
+
"""Serve the main page."""
|
|
46
|
+
return templates.TemplateResponse(request, "index.html")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@app.get("/api/generate")
|
|
50
|
+
async def generate(
|
|
51
|
+
username: str = Query(..., min_length=1, description="GitHub username"),
|
|
52
|
+
strategy: str = Query("random", description="Animation strategy"),
|
|
53
|
+
):
|
|
54
|
+
"""Generate and return a space shooter GIF."""
|
|
55
|
+
token = os.getenv("GH_TOKEN")
|
|
56
|
+
if not token:
|
|
57
|
+
raise HTTPException(status_code=500, detail="GitHub token not configured")
|
|
58
|
+
|
|
59
|
+
if strategy not in STRATEGY_MAP:
|
|
60
|
+
raise HTTPException(
|
|
61
|
+
status_code=400,
|
|
62
|
+
detail=f"Invalid strategy. Choose from: {', '.join(STRATEGY_MAP.keys())}",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
gif_buffer = generate_gif(username, strategy, token)
|
|
67
|
+
return Response(
|
|
68
|
+
content=gif_buffer.getvalue(),
|
|
69
|
+
media_type="image/gif",
|
|
70
|
+
headers={
|
|
71
|
+
"Response-Type": "blob",
|
|
72
|
+
"Content-Disposition": f"inline; filename={username}-space-shooter.gif"
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
except GitHubAPIError as e:
|
|
76
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
77
|
+
except Exception as e:
|
|
78
|
+
raise HTTPException(status_code=500, detail=f"Failed to generate GIF: {e}")
|