kot-video-api is a Kotlin + Spring Boot service that streams video files over HTTP with support for byte-range (RFC 7233-style) requests. This enables media players to seek efficiently by requesting only the required portions of a file.
The default branch (master) includes:
- Video streaming via
GET /videos/start(HTTP Range + conditional request support) - Playback tracking (sessions + event ingestion) backed by Postgres + Flyway
The playback tracking work was originally developed on feature/add-playback-sessions-api and has since been merged into master.
- HTTP Range Requests: supports
Range: bytes=...for partial content responses. - Multi-range Responses: supports multiple requested ranges (multipart/byteranges).
- Conditional Requests:
If-None-Match(ETag-based)If-Modified-SinceIf-MatchIf-Unmodified-Since
- ETag + caching headers: sets
ETag,Last-Modified,Expires,Accept-Ranges. - Rate limiting (server-side): streams through a Guava
RateLimiter(currently configured to ~5 Mbps).
- Playback sessions API:
POST /playback/sessions→ returnssession_id(authenticated). - Event ingestion API:
POST /playback/eventssupports batched ingestion with validation and dedupe. - Persistence: Postgres tables for sessions/events/idempotency + Flyway migrations.
- Integration tests: Testcontainers-based Postgres tests.
See: docs/PLAYBACK_TRACKING.md
- Java 21 (Gradle toolchain)
- Docker (required for Testcontainers integration tests)
- Postgres (required to run the app with playback tracking enabled; see configuration below)
git clone https://github.com/SaumilP/kot-video-api.git
cd kot-video-apiFor local dev you’ll typically want a running Postgres instance, or you can set spring.flyway.enabled=false and skip playback endpoints.
Using the Gradle wrapper:
./gradlew bootRunBy default the server runs on:
server.port=8082
Configuration lives in:
src/main/resources/application.properties
video.home controls the directory where video files are served from:
video.home=/path/to/your/videos/Override examples:
- By editing
src/main/resources/application.properties - Or via environment variables (Spring Boot relaxed binding), e.g.:
VIDEO_HOME=/path/to/videos
Note: the streaming endpoint expects a file name passed via query param, and constructs the full path as
video.home + fileName.
The app expects a Postgres database (for playback tracking tables + Flyway migrations). Defaults:
spring.datasource.url=jdbc:postgresql://localhost:5432/kot_video_api
spring.datasource.username=kot_video_api
spring.datasource.password=kot_video_apiRecommended overrides (env vars):
DB_URLDB_USERDB_PASSWORD
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migrationSession creation is protected via HTTP Basic auth. Defaults:
spring.security.user.name=dev
spring.security.user.password=devOverride (env vars):
APP_USERAPP_PASSWORD
Streams a file relative to video.home.
Example (full file):
curl -v "http://localhost:8082/videos/start?fl=myvideo.mp4"Example (range request for seeking):
curl -v \
-H "Range: bytes=0-999999" \
"http://localhost:8082/videos/start?fl=myvideo.mp4" \
--output part.binExample (player-style request):
curl -v \
-H "Accept: video/mp4" \
-H "Range: bytes=2000000-" \
"http://localhost:8082/videos/start?fl=myvideo.mp4" \
--output tail.bin- If the file does not exist: 404
- If the
Rangeheader is invalid: 416 withContent-Range: bytes */<length> - For a single satisfiable range: 206 Partial Content
- For multiple ranges: 206 and
Content-Type: multipart/byteranges; boundary=...
See docs/PLAYBACK_TRACKING.md for full contracts.
- Kotlin (JVM)
- Spring Boot (Web, Validation, JDBC, Security)
- Postgres + Flyway
- Gradle (Kotlin DSL)
- Guava (RateLimiter)
- JUnit + Testcontainers
Detailed structure docs:
docs/REPOSITORY_STRUCTURE.mddocs/ARCHITECTURE.mddocs/PLAYBACK_TRACKING.md
Quick view:
build.gradle.kts— Gradle build (Spring Boot + Kotlin + DB deps)src/main/kotlin/org/sandcastle/apps/KotVideoApiApplication.kt— Spring Boot entrypoint +/videoscontrollersrc/main/kotlin/org/sandcastle/apps/MultipartFileSender.kt— main streaming/range logicsrc/main/kotlin/org/sandcastle/apps/Range.kt— byte-range parsing + copy helpers + rate limitingsrc/main/kotlin/org/sandcastle/apps/HttpUtils.kt— basic header matching helperssrc/main/kotlin/org/sandcastle/apps/playback/**— playback session + events ingestionsrc/main/resources/db/migration/**— Flyway migrations
See architecture diagrams:
docs/ARCHITECTURE.md
- Fork the repo
- Create a feature branch:
git checkout -b feature/my-change - Run tests:
./gradlew test - Open a PR
Guidelines:
- Keep changes focused and well-tested.
- Prefer small, reviewable PRs.
docs/ARCHITECTURE.md— diagrams + component overviewdocs/REPOSITORY_STRUCTURE.md— full repo tree with explanationsdocs/PLAYBACK_TRACKING.md— playback session/events API + schema details
- The streaming implementation uses common HTTP range-serving patterns adapted for Kotlin/Spring Boot.
- Guava
RateLimiteris used to throttle streaming throughput.
This changelog is derived from git history.
-
2024-04-14 —
d4d1a32— “Functional api”
Baseline video streaming API. -
2026-03-02 —
e5d3143— “Feature 🏗️ : Add playback APIs”
Adds playback tracking APIs + Postgres/Flyway/Testcontainers + build upgrades. -
2026-03-02 —
7f61299— “Merge pull request #18 …”
Merges the playback tracking branch intomaster.