Files
Nicholas Ward 518324a58c
Build server image / build (push) Successful in 1m8s
Release Android APK / release (push) Successful in 1m33s
Trust the WARD certificate authority on Android
2026-09-22 11:27:27 -05:00

3.6 KiB

short-form-video

A rapid prototype for browsing a private video library as a thumbnail grid and playing its videos in a TikTok-style vertical pager. Content identity is independent of filesystem layout, so videos can be moved or renamed safely.

Run the server

The server reads its media directory through a read-only bind mount. Create a local configuration file, generate a unique bearer token, and start it with:

cp .env.example .env
sed -i "s/replace-with-a-long-random-token/$(openssl rand -hex 32)/" .env
docker compose up --build

Edit MEDIA_PATH in .env if your videos are not in sample-media. The .env file is ignored by Git; never commit a real API token.

The API is available at http://localhost:8080, requires bearer authentication, and rescans every minute.

You can also override either value for a single invocation:

API_TOKEN="$(openssl rand -hex 32)" MEDIA_PATH="$PWD/sample-media" docker compose up --build

The SQLite catalog lives in the catalog-data Docker volume. Video files are mounted read-only. SHA-256 content identity survives file moves and renames. Identical files are represented as one logical video. Each scan synchronizes read-only collections with the directories containing the videos.

Run the Android app

Open the repository in Android Studio and run the android:app configuration, or build from a shell with JDK 17 and an Android SDK:

./gradlew :android:app:assembleDebug

Use http://10.0.2.2:8080 from the Android emulator. On a physical device, use the development computer's LAN address, such as http://192.168.1.20:8080. Enter the same API token when connecting. The app stores its connection settings in its private application data and excludes them from Android backups. Cleartext HTTP is permitted only in debug builds; use HTTPS for non-local deployments. The release app trusts Android's system roots and the bundled public WARD root CA used by the project's private PKI.

Tap a grid tile to open the vertical player. Long-press to select videos for a manual collection. The pager prepares neighboring pages to reduce startup latency. Swipe horizontally between the library and collections, or pull down to refresh.

Development checks

cd server && go test ./...
docker compose config --quiet
./gradlew :android:app:assembleDebug

ffmpeg and ffprobe are included in the server image for thumbnails and media metadata. The server otherwise uses Go's standard library plus one SQLite driver.

Container publishing

The Gitea workflow expects these repository settings:

  • Variables: IMAGE_REGISTRY and IMAGE_NAME
  • Secrets: REGISTRY_USERNAME and REGISTRY_PASSWORD

Forks do not inherit those secrets. Configure them before enabling the workflow on a new repository.

Android releases

Pushing a tag such as v0.1.0 builds a signed release APK and attaches it, along with a SHA-256 checksum, to a Gitea Release. The workflow requires these repository secrets:

  • ANDROID_SIGNING_KEYSTORE_BASE64
  • ANDROID_SIGNING_STORE_PASSWORD
  • ANDROID_SIGNING_KEY_ALIAS
  • ANDROID_SIGNING_KEY_PASSWORD
  • RELEASE_TOKEN with repository write access

Keep an offline backup of the signing keystore and its passwords. Every future update must be signed with the same key. Increase the version tag for each release; the workflow derives the user-visible version from the tag.

Security

Treat the bearer token as a password. The server provides a single shared token, not per-user accounts or authorization scopes, and should not be exposed directly to the internet without HTTPS and appropriate network controls.