Skip to content

Repository files navigation

zplex-sync

Standalone indexer / ingest service for ZPlex Labs. It is a one-shot batch job (run on a schedule) that walks the Google Drive media libraries, enriches each title with metadata from TMDB and OMDB, writes the catalog into PostgreSQL, and refreshes the filter caches in Redis/Valkey (RedisJSON). The API and stream services read what this job produces — this is the write side of the CQRS split.

Google Drive ──▶ zplex-sync ──▶ PostgreSQL (catalog)
   TMDB/OMDB ──▶ (enrich)   ──▶ Redis/Valkey (filter caches, RedisJSON)

How it runs

# Build the fat jar
./gradlew build            # produces build/libs/zplex-sync.jar

# Run (prod profile = application.properties only)
java -jar build/libs/zplex-sync.jar

# Run with the dev overrides (application.dev.properties)
java -jar build/libs/zplex-sync.jar --dev

Entry point: zechs.zplex.sync.MainKtIndexingService() runs genres, movies, shows, then filter caches, and exits.

The schema in src/main/resources/setup-db.sql also defines the read-side query functions search_media and count_media. Both accept trailing per-user access params (p_allowed_ratings — NULL means no rating ceiling, p_allow_unrated, p_unrated_ratings, p_blacklist_ids) so zplex-api can filter catalog listings by rating rank and hide blacklisted titles. The daily suggestions function fetch_titles_for_today takes matching access params (p_allow_movies, p_allow_shows, p_allowed_ratings, p_allow_unrated, p_unrated_ratings, p_movie_blacklist, p_show_blacklist) so per-user library flags, rating ceiling, and per-type blacklist are applied to the two-type suggestion pool.

Configuration

Config is layered: application.properties (base) is overridden by application.<profile>.properties when --dev is passed. Values support ${ENV_VAR:default} placeholders resolved from environment variables first, then JVM properties, then other properties.

Key Purpose
tmdb.api.key / omdb.api.key Metadata provider keys
movies.folder / shows.folder Google Drive library folder IDs
google.drive.client.email / client.id / private.key / private.key.id Drive service-account credentials
zplex.db.host / port / username / password PostgreSQL catalog store
zplex.cache.host / port / username / password Redis/Valkey (TLS) filter cache

Environment variables used now

All placeholders in src/main/resources/application.properties are required today (no defaults are defined there).

Variable Required Purpose
GOOGLE_DRIVE_CLIENT_EMAIL Yes Service account email for Drive API JWT auth.
GOOGLE_DRIVE_CLIENT_ID Yes Service account client id.
GOOGLE_DRIVE_PRIVATE_KEY_ID Yes Service account private key id.
GOOGLE_DRIVE_PRIVATE_KEY_PKCS8 Yes Service account private key (supports escaped \\n).
MOVIES_FOLDER Yes Google Drive folder id for movies library root.
SHOWS_FOLDER Yes Google Drive folder id for shows library root.
ZPLEX_DATABASE_HOST Yes PostgreSQL host.
ZPLEX_DATABASE_PORT Yes PostgreSQL port.
ZPLEX_DATABASE_USERNAME Yes PostgreSQL username.
ZPLEX_DATABASE_PASSWORD Yes PostgreSQL password.
ZPLEX_CACHE_HOST Yes Redis/Valkey host.
ZPLEX_CACHE_PORT Yes Redis/Valkey port.
ZPLEX_CACHE_USERNAME Yes Redis/Valkey username.
ZPLEX_CACHE_PASSWORD Yes Redis/Valkey password.
OMDB_API_KEY Yes OMDB API key for metadata enrichment.
TMDB_API_KEY Yes TMDB API key for metadata enrichment.

Tech stack

Concern Library Version
Language Kotlin (JVM 21) 2.3.21
Build Gradle 9.7.0
Fat jar GradleUp Shadow 9.6.1
Codegen KSP 2.3.11
Redis/Valkey Jedis 8.0.0
HTTP OkHttp 5.4.0
REST Retrofit 3.0.0
JSON Moshi 1.15.2
DB driver PostgreSQL 42.7.13
Coroutines kotlinx-coroutines 1.11.0
Google Drive google-api-services-drive v3-rev20260720-2.0.0

Dependency notes

  • Kotlin is pinned to 2.3.21 (latest with a matching stable KSP 2.3.11); Kotlin 2.4.x has no released KSP yet.
  • Shadow moved to the GradleUp coordinates (com.gradleup.shadow); the task class namespace stayed com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.
  • Jedis 8 removed JedisPooled. The client is now built via the recommended RedisClient.builder() family (see RedisConfig), and TLS is configured through SslOptions instead of the deprecated .ssl(true) setter. TLS trust uses the JVM default truststore — add your provider CA if it is not publicly trusted.

No tests

This service ships without a test suite by project policy; verify changes with ./gradlew build and a manual/dry run.

About

Cron-job to periodically sync zplex-db

Resources

Stars

0 stars

Watchers

1 watching

Forks

Used by

Contributors

Languages