Table of Contents generated automatically
- Impartus CLI
A Go-based CLI and HTTP API server for downloading lecture videos from Impartus platforms. Features interactive mode for humans and deterministic JSON mode for automation and AI agents.
- Interactive CLI Mode - Guided download flow with course/lecture selection
- Deterministic JSON Mode - Machine-readable output for automation and AI agent integration
- HTTP API with WebSocket Events - REST API with real-time job progress updates
- Multi-View Video Processing - Support for instructor/dual-view video streams
- AES Encrypted Chunk Handling - Automatic decryption of Impartus video chunks
- Pipeline Parallelization - Concurrent download + decrypt for faster throughput
- Progress Tracking with ETA - Real-time progress bars with speed and time estimates
- Rate Limiting - Configurable API and download rate limits
- Slide Download Support - Download lecture slides alongside video content
# Install from source
go install github.com/rabesss/impartus-cli@latest
# Or run the container package
docker run --rm ghcr.io/rabesss/impartus-cli:main --help
# Or download the latest release asset
gh release download --repo rabesss/impartus-cli --pattern 'impartus_*_linux_amd64.tar.gz'
# Or build from source
git clone https://github.com/rabesss/impartus-cli
cd impartus-cli
go build -o impartus .- Go 1.25+ - Go toolchain for building (pinned in
go.mod; Docker images may use a newer patch release) - FFmpeg - Required for video processing (must be in
PATH) - mpv - Required for the
playcommand (must be inPATH) - Impartus Account - Valid credentials for your institution's Impartus platform
The --help command above is a quick image and entrypoint smoke test. For a
real download, mount the configuration read-only and provide writable download
and temporary directories:
mkdir -p downloads temp
docker run --rm \
--volume "$PWD/config.json:/work/config.json:ro" \
--volume "$PWD/downloads:/work/downloads" \
--volume "$PWD/temp:/work/temp" \
ghcr.io/rabesss/impartus-cli:main \
download --subject 123 --session 456The image runs as the non-root impartus user. Bind-mounted downloads and
temp directories retain their host ownership and permissions, so they must be
writable by that container user. API jobs are stored in /work/.jobs.json; the
file is ephemeral when the container is removed unless /work is persisted.
- Create a private configuration file:
make config-initThis copies the sample when config.json does not exist and sets owner-only
permissions (0600). If the file already exists, the command only tightens its
permissions and never overwrites your configuration. You can apply the same
protection manually with chmod 600 config.json.
- Edit
config.jsonwith your credentials:
{
"username": "your_impartus_email@example.com",
"password": "your_impartus_password",
"baseUrl": "https://a.impartus.com/api",
"quality": "720",
"views": "both",
"downloadLocation": "./downloads"
}| Field | Type | Required | Default | Description |
|---|---|---|---|---|
username |
string | Yes | - | Impartus username (email) |
password |
string | Yes | - | Impartus password |
baseUrl |
string | Yes | - | Impartus API base URL |
quality |
string | No | "720" |
Video quality: 144, 450, 720 |
views |
string | No | "both" |
Views: left, right, both, first, second |
downloadLocation |
string | No | "./downloads" |
Output directory |
tempDirLocation |
string | No | "./temp" |
Temporary directory |
slides |
bool | No | false |
Download slides alongside video |
audioOnly |
bool | No | false |
Download audio only |
audioFormat |
string | No | "mp3" |
Audio format: mp3, m4a, aac, opus |
numWorkers |
int | No | 5 |
Concurrent lecture workers (1-50); active playlist downloads are bounded by per-lecture media workers to preserve the browser-observed burst envelope |
rateLimit |
float | No | 100 |
Download rate limit (0.1-100 req/sec) |
apiRateLimit |
float | No | 2 |
API rate limit (0.1-20 req/sec) |
enablePipeline |
bool | No | false |
Enable concurrent download+decrypt |
downloadWorkersPerLecture |
int | No | 12 |
Download workers per lecture (1-12) |
decryptWorkersPerLecture |
int | No | 4 |
Decrypt workers per lecture (1-10) |
httpTimeout |
string | No | "10m" |
Timeout for the shared upstream HTTP client, including login, API, playlist, and media requests (30s-60m) |
enableJitter |
bool | No | true |
Add small random delays to API requests |
skipNoAudio |
bool | No | false |
Skip lectures with no audio track |
listenAddr |
string | No | "127.0.0.1" |
API server bind address (loopback only unless allowRemoteAccess is set) |
allowRemoteAccess |
bool | No | false |
Permit a non-loopback listenAddr (e.g. 0.0.0.0); required to expose the API on the network |
progressTracking |
object | No | see below | Progress bar tracking configuration |
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enable all progress bars in human-readable mode; JSON mode remains quiet |
showSpeed |
bool | false |
Include download speed in the aggregate progress status |
showETA |
bool | false |
Include estimated time remaining in the aggregate progress status |
updateInterval |
string | "2s" |
Speed-sampling interval (500ms-10s) |
speedWindowSize |
int | 10 |
Number of samples used for the speed moving average (3-30) |
Only the settings listed below have environment-variable overrides. Settings absent from this table must be configured in JSON.
| Environment variable | Config field | Notes |
|---|---|---|
IMPARTUS_USERNAME |
username |
Required unless supplied in JSON |
IMPARTUS_PASSWORD |
password |
Required unless supplied in JSON |
IMPARTUS_BASE_URL |
baseUrl |
Required unless supplied in JSON |
IMPARTUS_QUALITY |
quality |
144, 450, or 720 |
IMPARTUS_VIEWS |
views |
left, right, both, first, or second |
IMPARTUS_DOWNLOAD_LOCATION |
downloadLocation |
Output directory |
IMPARTUS_TEMP_DIR |
tempDirLocation |
Temporary directory |
IMPARTUS_TEMP_DIR_LOCATION |
tempDirLocation |
Compatibility alias for the shorter temporary-directory variable |
IMPARTUS_AUDIO_FORMAT |
audioFormat |
mp3, m4a, aac, or opus |
IMPARTUS_HTTP_TIMEOUT |
httpTimeout |
Go duration between 30s and 60m |
IMPARTUS_LISTEN_ADDR |
listenAddr |
Non-loopback values also require remote-access opt-in |
IMPARTUS_AUDIO_ONLY |
audioOnly |
Boolean |
IMPARTUS_SLIDES |
slides |
Boolean |
IMPARTUS_SKIP_NO_AUDIO |
skipNoAudio |
Boolean |
IMPARTUS_ALLOW_REMOTE_ACCESS |
allowRemoteAccess |
Boolean |
IMPARTUS_ENABLE_JITTER |
enableJitter |
Boolean |
IMPARTUS_PROGRESS_TRACKING_ENABLED |
progressTracking.enabled |
Boolean; controls all progress bars |
IMPARTUS_NUM_WORKERS |
numWorkers |
Integer from 1-50 |
IMPARTUS_RATE_LIMIT |
rateLimit |
Number from 0.1-100 |
IMPARTUS_API_RATE_LIMIT |
apiRateLimit |
Number from 0.1-20 |
usernameandpasswordare requiredbaseUrlmust be a valid URLqualitymust be one of:144,450,720viewsmust be one of:left,right,both,first,secondnumWorkersmust be between 1-50rateLimitmust be between 0.1-100httpTimeoutmust be between 30s-60m
Run without arguments for guided download:
./impartusThis launches an interactive workflow:
- Log in with configured credentials
- Select course from list
- Select session/lecture range
- Download with progress tracking
Pass --json for machine-readable output:
# Get capability metadata
./impartus --json
# List courses
./impartus courses --json
# List lectures
./impartus lectures -s 123 -S 456 --jsonResponse envelope:
{
"success": true,
"data": {},
"error": null,
"meta": {
"command": "courses",
"mode": "json"
}
}Successful JSON commands write exactly one response envelope to stdout. They do
not write progress bars or warning text, and successful downloads leave stderr
empty. Failed JSON commands exit non-zero and write exactly one error envelope
to stderr while leaving stdout empty; in that envelope, success is false,
data is null, and error.message contains the error text.
For JSON downloads, lectureCount is the number of lectures completed.
outputPaths contains the files produced, so one completed lecture can add
multiple paths when multiple views or output forms are requested.
| Command | Description |
|---|---|
impartus |
Interactive mode (guided download) |
impartus --json |
Capability metadata |
impartus help |
Show usage information |
impartus version |
Show version and build date |
impartus courses |
List available courses |
impartus lectures -s ID -S ID |
List lectures for subject/session |
impartus download [flags] |
Download lectures |
impartus play [flags] |
Play lectures in mpv |
impartus serve [--port PORT] |
Start HTTP API server |
./impartus download --subject 123 --session 456 [flags]
./impartus play --subject 123 --session 456 [flags]| Flag | Short | Description | Applicable To |
|---|---|---|---|
--subject |
-s |
Subject ID (required) | Both |
--session |
-S |
Session ID (required) | Both |
--start |
Start lecture index (1-based) | Both | |
--end |
End lecture index (1-based, inclusive) | Both | |
--lecture |
-l |
Specific lecture index (shortcut for start & end) | Play Only |
--quality |
Quality: 144, 450, 720 |
Both | |
--views |
Views: left, right, both, first, second |
Both | |
--audio-only |
Audio-only mode | Download Only | |
--format |
Audio format: mp3, m4a, aac, opus |
Download Only | |
--output |
-o |
Output directory | Download Only |
--json |
JSON output (non-blocking) | Download Only |
Examples:
# Download lectures 1-5 from course
./impartus download -s 123 -S 456 --start 1 --end 5
# Download in 720p quality
./impartus download -s 123 -S 456 --quality 720
# Download audio only
./impartus download -s 123 -S 456 --audio-only --format mp3
# Download to custom directory
./impartus download -s 123 -S 456 -o /path/to/output
# Play lectures 1-5 from course
./impartus play -s 123 -S 456 --start 1 --end 5
# Play a specific lecture
./impartus play -s 123 -S 456 --lecture 3Start the HTTP API server with job persistence:
# Jobs are persisted to .jobs.json and survive server restarts
./impartus serveJob Persistence: Jobs are automatically saved to .jobs.json. Running/pending jobs at shutdown are restored as failed (non-resumable). Completed/failed/canceled jobs are restored with their preserved state.
# Default port 8080
./impartus serve
# Custom port
./impartus serve --port 9090
# JSON metadata (non-blocking)
./impartus serve --json# Login
curl -X POST http://localhost:8080/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"your_user", "password":"your_pass"}'Response:
{
"success": true,
"data": {
"token": "eyJ...",
"expires": "2025-02-12T12:34:56Z"
}
}Use the token for authenticated requests:
curl -H "Authorization: Bearer <token>" http://localhost:8080/api/v1/courses| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/v1/health/live |
No | Process liveness check |
GET |
/api/v1/health/ready |
No | Cached dependency readiness check |
GET |
/api/v1/health |
No | Compatibility alias for readiness |
POST |
/api/v1/auth/login |
No | Authenticate |
GET |
/api/v1/courses |
Yes | List courses |
GET |
/api/v1/lectures |
Yes | List lectures |
POST |
/api/v1/jobs |
Yes | Create download job |
GET |
/api/v1/jobs |
Yes | List all jobs |
GET |
/api/v1/jobs/{id} |
Yes | Get job status |
DELETE |
/api/v1/jobs/{id} |
Yes | Cancel job |
GET |
/api/v1/ws |
Yes | WebSocket events |
# Use this for process or container liveness probes (no dependency checks)
curl http://localhost:8080/api/v1/health/live
# Use this for dependency readiness
curl http://localhost:8080/api/v1/health/ready/api/v1/health/live returns the standard envelope with data.status set to ok and performs no configuration, network, token-cache, filesystem, or executable checks.
/api/v1/health/ready returns a structured {success, data, error, meta} envelope with sub-checks for config, upstream, and FFmpeg status. /api/v1/health remains a compatibility alias with the same readiness response:
{
"success": true,
"data": {
"status": "ok",
"config": {
"status": "ok"
},
"upstream": {
"status": "reachable"
},
"ffmpeg": {
"status": "available"
}
},
"error": null,
"meta": {
"command": "health",
"mode": "api"
}
}Status values:
config.status:ok(all fields set) ormisconfigured(missing fields)upstream.status:reachable(server responds),unreachable(TCP/HTTP fails), ornot_configured(no baseUrl)ffmpeg.status:available(in PATH) ornot_found- Any sub-check may report
unknownif readiness probing fails internally; inspect server logs for details - Overall
status:ok(all sub-checks pass) ordegraded(one or more sub-checks fail)
The unauthenticated health response deliberately exposes only aggregate configuration status; it does not reveal which credential fields are present.
Readiness results, including degraded results, are cached for 15 seconds and may be that old. Readiness endpoints retain HTTP 200 when degraded, so callers must inspect data.status rather than relying on the HTTP status alone.
Idempotency Key Support: Pass an optional idempotencyKey field to prevent duplicate job creation on network retries. If a job with the same key already exists, returns the existing job with HTTP 409 Conflict.
curl -X POST http://localhost:8080/api/v1/jobs \
-H "Authorization: Bearer <token>" \
-H 'Content-Type: application/json' \
-d '{
"subjectId": 123,
"sessionId": 456,
"startIndex": 1,
"endIndex": 5,
"idempotencyKey": "unique-identifier-here",
"jobConfig": {
"quality": "720",
"views": "both",
"enablePipeline": true,
"numWorkers": 6
}
}'Note: API uses 1-based indexing for startIndex and endIndex (inclusive), matching CLI --start and --end.
Connect to receive real-time job updates:
import WebSocket from 'ws';
const ws = new WebSocket('ws://localhost:8080/api/v1/ws', {
headers: {
Authorization: `Bearer ${token}`
}
});
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(`Event: ${data.type}`, data);
};| Event | Description |
|---|---|
job.started |
Job began execution |
job.progress |
Progress update (includes phase and percentage) |
job.completed |
Job finished successfully |
job.failed |
Job failed with error |
job.cancelled |
Job was cancelled |
See docs/websocket-events.md for complete event schemas.
WebSocket events are live notifications, not a durable event stream. A client
that stops reading may be disconnected when its bounded outbound queue fills.
Reconnect and query GET /api/v1/jobs/{id} to recover the current job state;
events that occurred while disconnected are not replayed.
# Build
make build
# Run tests
make test
# Run linter
make lint
# Run pre-commit hooks
make pre-commit
| Target | Description |
|---|---|
make build |
Build the impartus binary |
make test |
Run tests with coverage |
make lint |
Run golangci-lint |
make pre-commit-install |
Install pre-commit hooks |
make pre-commit |
Run pre-commit on all files |
make clean |
Clean build artifacts |
make install |
Install to $GOPATH/bin |
make run-cli |
Run CLI interactive mode |
make run-api |
Start API server on port 8080 |
make docs |
Generate docs table of contents |
make docs-toc |
Generate documentation table of contents |
make security |
Run all security scans (gitleaks, gosec, trivy, govulncheck) |
make security-gitleaks |
Run secret scanning |
make security-gosec |
Run Go security analysis |
make security-trivy |
Run vulnerability scanning |
make security-govulncheck |
Run Go vulnerability check |
Install development tools:
# Install golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin
# Install pre-commit
pip install pre-commit
pre-commit install# All tests
go test ./...
# With coverage
go test ./... -cover -coverprofile=coverage.out
go tool cover -func=coverage.out
# Verbose
go test ./... -vThis project is CLI-first, API-secondary: the CLI is the primary execution path, and the API is started from impartus serve when needed.
impartus/
├── main.go # Root entrypoint
├── cmd/impartus/main.go # Module-style entrypoint
├── internal/
│ ├── cli/ # Command routing and implementations
│ ├── config/ # Configuration parsing and validation
│ ├── client/ # Impartus API client, auth, HTTP helpers
│ ├── downloader/ # Playlist parsing, chunk download/decrypt, ffmpeg
│ └── server/ # HTTP API, auth middleware, jobs, WebSocket
├── docs/ # Documentation
└── config.json # User configuration
internal/cli- CLI command routing and interactive/deterministic modesinternal/config- Configuration loading, defaults, and validationinternal/client- Impartus API HTTP client with authenticationinternal/downloader- Video pipeline: playlist parsing, chunk download, AES decryption, FFmpeg joininternal/server- HTTP API server with bearer-token auth, background jobs, and WebSocket broadcasting
For detailed flow diagrams, see docs/architecture.md.
See CONTRIBUTING.md for local setup, PR guidelines, and code style expectations.
Security-sensitive changes are described in SECURITY.md.
MIT License - see LICENSE for details.
- gorilla/mux - HTTP router
- gorilla/websocket - WebSocket implementation
- vbauerster/mpb - Progress bars
- google/uuid - UUID generation
- golang.org/x/time - Rate limiting
CONTRIBUTING.md- Contributing guideSECURITY.md- Security policydocs/architecture.md- Architecture and flow diagramsdocs/api-reference.md- REST API documentationdocs/websocket-events.md- WebSocket event schemasdocs/error-codes.md- Error code referencedocs/runbooks.md- Incident response and troubleshooting