# DicomPressor API Documentation

**Base URL:** `https://dicompressor.sitnov.work`

---

## Authentication

All API endpoints require an **API key**. Pass it as:

| Method | Example |
|--------|---------|
| HTTP Header (recommended) | `X-API-Key: your-key-here` |
| Query parameter | `?api_key=your-key-here` |

> **Note:** The Web Tool (browser UI at `/app`) does not require an API key.
> API keys are required only for programmatic (script/code) access.

**To get an API key**, contact the administrator at **nsitnov@gmail.com**.
Include your name, organization, and intended use case.

---

## Workflow

Every processing job follows this flow:

```
1. Create Session  →  2. Upload Files  →  3. Process  →  4. Download / View
```

Sessions are temporary and automatically expire after **1 hour**.

---

## Endpoints

### POST /api/session

Creates a new processing session.

**Response:**
```json
{
  "session_id": "c256553e-b352-4280-af2c-21cb8bfb1f64"
}
```

### POST /api/upload/{session_id}

Upload DICOM files. Use `multipart/form-data` with field name `files`.

| Parameter | Type | Description |
|-----------|------|-------------|
| `session_id` | path | Session UUID from `/api/session` |
| `files` | form-data | One or more DICOM files |

**Response:**
```json
{
  "uploaded": 400,
  "files": ["0000.dcm", "0001.dcm", "..."]
}
```

> **Tip:** For large uploads, send 20–50 files per request.
> Max upload: **500 MB** per request. Max files per session: **2000**.

### GET /api/files/{session_id}

List all input and output files in the session.

**Response:**
```json
{
  "input": [
    {"name": "0000.dcm", "size": 406602}
  ],
  "output": [
    {"name": "merged_multiframe.dcm", "size": 162068266}
  ]
}
```

### POST /api/process/{session_id}

Run a processing action. Send JSON body.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `action` | string | Yes | One of the available actions (see below) |
| `params` | string | Only for `anonymize` | DICOM tag=value, one per line |

**Response:**
```json
{
  "success": true,
  "action": "merge",
  "log": "[INFO] Processing 400 files...\n[INFO] Done.",
  "output_files": [
    {"name": "Patient_multiframe.dcm", "size": 162068266}
  ]
}
```

> **Timeout:** Processing has a **5 minute** timeout.

### GET /api/download/{session_id}/{filename}

Download a result file. Returns file as `application/octet-stream`.

### GET /api/view/{session_id}/{filename}

Serve a file inline for VolView 3D viewer. Returns `application/dicom`.

---

## Available Actions

| Action | CLI Flag | Description | Input | Output |
|--------|----------|-------------|-------|--------|
| `merge` | `-j` | Merge single-frame slices into multi-frame | Multiple .dcm | Single .dcm |
| `split` | `-s` | Split multi-frame into individual frames | Single .dcm | Multiple .dcm |
| `compress_lossless` | `-x` | JPEG 2000 lossless compression | .dcm files | Compressed .dcm |
| `compress_lossy` | `-z` | JPEG 2000 lossy compression | .dcm files | Compressed .dcm |
| `decompress` | `-u` | Remove compression | .dcm files | Uncompressed .dcm |
| `anonymize` | `-a` | Replace patient identifiers | .dcm + params | Anonymized .dcm |
| `export_png` | `-I png` | Export frames as PNG images | .dcm files | .png images |
| `export_video` | `-E` | Export multi-frame as MP4 | Single .dcm | .mp4 video |
| `dicomdir` | `-d` | Create DICOMDIR index | .dcm files | DICOMDIR file |
| `headers` | `-t` | Export DICOM headers | .dcm files | Modified .dcm |
| `info` | `--info` | Show file information | Single .dcm | Text (in `log`) |
| `summary` | `--summary` | Show folder summary | .dcm files | Text (in `log`) |

---

## CLI: Scheduler & Watch Mode

DicomPressor includes built-in support for scheduled/automated processing via two flags
and dedicated watch scripts.

### --skip-if-done

Adds idempotent processing: creates a `.dicompressor_done` JSON marker file after success.
On subsequent runs, skips folders that already have the marker. Safe for cron jobs.

```bash
# Merge, but skip if already processed
python3 dicompressor.py -j --skip-if-done -f /path/to/patient_folder

# Second run — instantly skips
python3 dicompressor.py -j --skip-if-done -f /path/to/patient_folder
# Output: SKIPPED (already processed)

# To re-process, delete the marker:
rm /path/to/patient_folder/.dicompressor_done
```

### --watch N

Continuously monitors a parent folder for new patient subfolders and auto-merges every N seconds.
Implies `--skip-if-done`. Press Ctrl+C to stop.

```bash
# Watch /data/patients, merge new subfolders every 5 minutes:
python3 dicompressor.py -j --watch 300 -f /data/patients/
```

### --output-dir DIR

Copy merged result files to a separate directory. Works with `-j`, `--skip-if-done`, and `--watch`.
The directory is auto-created if missing.

```bash
# Watch + copy all merged files to a central folder:
python3 dicompressor.py -j --watch 300 --output-dir /data/merged -f /data/patients/

# Single merge + copy result:
python3 dicompressor.py -j --output-dir /data/merged -f /path/to/patient_folder
```

### Standalone Watch Scripts

For production use, dedicated watch scripts are included in the distribution:

**Linux / macOS / WSL (Bash):**
```bash
# Basic watch:
./dicompressor-watch.sh /data/patients 300

# Watch + output dir:
./dicompressor-watch.sh /data/patients 300 /data/merged
```

**Windows (PowerShell):**
```powershell
# Basic watch:
.\dicompressor-watch.ps1 -WatchDir "D:\DICOM\Patients" -IntervalSeconds 300

# Watch + output dir:
.\dicompressor-watch.ps1 -WatchDir "D:\DICOM\Patients" -OutputDir "D:\Merged"
```

Both scripts scan for `.dcm` files in subfolders, skip already-processed folders
(marker file), and log progress with timestamps.

---

## Full Examples

### cURL — Complete Merge Workflow

```bash
#!/bin/bash
API="https://dicompressor.sitnov.work"
KEY="YOUR_KEY"

# 1. Create session
SESSION=$(curl -s -X POST -H "X-API-Key: $KEY" $API/api/session | jq -r .session_id)
echo "Session: $SESSION"

# 2. Upload all .dcm files
for f in ./dicoms/*.dcm; do
    curl -s -X POST -H "X-API-Key: $KEY" -F "files=@$f" $API/api/upload/$SESSION > /dev/null
done

# 3. Merge
RESULT=$(curl -s -X POST \
  -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"action":"merge"}' \
  $API/api/process/$SESSION)

echo "Success: $(echo $RESULT | jq .success)"

# 4. Download results
echo $RESULT | jq -r '.output_files[].name' | while read fname; do
    curl -s -H "X-API-Key: $KEY" -o "$fname" $API/api/download/$SESSION/$fname
done
```

### Python

```python
import requests
from pathlib import Path

API = "https://dicompressor.sitnov.work"
KEY = "YOUR_KEY"
HEADERS = {"X-API-Key": KEY}

# 1. Create session
session_id = requests.post(f"{API}/api/session", headers=HEADERS).json()["session_id"]

# 2. Upload files (batch of 20)
files = sorted(Path("./dicoms").glob("*.dcm"))
for i in range(0, len(files), 20):
    batch = files[i:i+20]
    form_files = [("files", (f.name, open(f, "rb"))) for f in batch]
    requests.post(f"{API}/api/upload/{session_id}", headers=HEADERS, files=form_files)

# 3. Process
result = requests.post(
    f"{API}/api/process/{session_id}",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={"action": "merge"}
).json()

# 4. Download
for f in result["output_files"]:
    r = requests.get(f"{API}/api/download/{session_id}/{f['name']}", headers=HEADERS)
    Path(f["name"]).write_bytes(r.content)
    print(f"Downloaded {f['name']} ({f['size'] / 1048576:.1f} MB)")
```

### Python — Anonymize with Custom Parameters

```python
result = requests.post(
    f"{API}/api/process/{session_id}",
    headers={**HEADERS, "Content-Type": "application/json"},
    json={
        "action": "anonymize",
        "params": """(0010,0010)=ANONYMOUS^PATIENT
(0010,0020)=ANON001
(0010,0030)=19000101
(0010,0040)=O
(0008,0090)=DR ANONYMOUS"""
    }
).json()
```

### PowerShell

```powershell
$API = "https://dicompressor.sitnov.work"
$Key = "YOUR_KEY"
$Headers = @{ "X-API-Key" = $Key }

# 1. Create session
$session = (Invoke-RestMethod -Uri "$API/api/session" `
    -Method POST -Headers $Headers).session_id

# 2. Upload files
$files = Get-ChildItem -Path ".\dicoms\*.dcm"
foreach ($file in $files) {
    $form = @{ files = Get-Item $file.FullName }
    Invoke-RestMethod -Uri "$API/api/upload/$session" `
        -Method POST -Headers $Headers -Form $form | Out-Null
}

# 3. Process
$body = @{ action = "merge" } | ConvertTo-Json
$result = Invoke-RestMethod -Uri "$API/api/process/$session" `
    -Method POST -Headers ($Headers + @{ "Content-Type" = "application/json" }) `
    -Body $body

# 4. Download
foreach ($f in $result.output_files) {
    Invoke-WebRequest -Uri "$API/api/download/$session/$($f.name)" `
        -Headers $Headers -OutFile $f.name
}
```

---

## Error Codes

| Code | Meaning | Common Cause |
|------|---------|--------------|
| 200 | Success | — |
| 400 | Bad Request | Invalid action, missing files |
| 401 | Unauthorized | Invalid or missing API key |
| 404 | Not Found | Invalid session ID or filename |
| 413 | Payload Too Large | Upload exceeds 500 MB |
| 429 | Too Many Requests | Rate limit exceeded |
| 503 | Service Unavailable | Max sessions reached |
| 504 | Gateway Timeout | Processing exceeded 5 min |

All errors return JSON: `{"error": "Description"}`

---

## Rate Limits

| Limit | Value |
|-------|-------|
| API requests per minute (per IP) | **30** |
| Session creation per minute (per IP) | **5** |
| Max concurrent sessions (server) | **50** |
| Max upload per request | **500 MB** |
| Max files per session | **2000** |
| Processing timeout | **5 minutes** |
| Session TTL (auto-delete) | **1 hour** |

---

## Get an API Key

Contact the administrator at **nsitnov@gmail.com**.

Include: your name, organization/project, and intended use case.
Keys are issued free of charge for research and medical use.
