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_REGISTRYandIMAGE_NAME - Secrets:
REGISTRY_USERNAMEandREGISTRY_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_BASE64ANDROID_SIGNING_STORE_PASSWORDANDROID_SIGNING_KEY_ALIASANDROID_SIGNING_KEY_PASSWORDRELEASE_TOKENwith 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.