diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..563ddf4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +**/node_modules +data +.git +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dd3e655 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM node:22-bookworm-slim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends android-tools-adb \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Install deps first so this layer is cached unless package.json changes. +COPY server/package.json server/package-lock.json ./server/ +RUN cd server && npm ci --omit=dev + +COPY server ./server +COPY public ./public + +WORKDIR /app/server +EXPOSE 8787 +CMD ["node", "src/index.js"] diff --git a/README.md b/README.md index 970cdf0..477ef22 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,15 @@ LAN. Add to the home screen and it behaves like an installed app. ## Requirements -- Node.js 18+ and `adb` (Android platform-tools) installed **on the machine - running the server** — not on the phone. - - macOS: `brew install android-platform-tools` - - Debian/Ubuntu: `sudo apt install android-tools-adb` (or - `android-sdk-platform-tools`) - - Windows: download "SDK Platform-Tools" from developer.android.com and - put `adb.exe` on PATH -- The TV and this server on the same local network. +- Docker and Docker Compose on any always-on machine on your home network (a + Pi, NAS, old laptop, home server). +- The TV and that machine on the same local network. + +`adb` does **not** need to run on bare metal — it's a plain TCP client that +dials out to the TV's IP once network/wireless debugging is on, so it works +fine from inside a container with normal bridge networking. No host +networking, USB passthrough, or privileged mode required. The container +image bundles `adb` itself, so nothing else needs installing on the host. ## Set up the TV @@ -51,32 +52,74 @@ LAN. Add to the home screen and it behaves like an installed app. ## Run the server ```sh -cd server -npm install -npm start +docker compose up -d --build ``` This serves the app at `http://:8787`. Open that URL on your phone, add your TV by IP in Settings (⚙), then optionally "Add to Home Screen" for a full-screen, app-like remote. -Override the port with `PORT=9000 npm start`, or point at a non-default -`adb` binary with `ADB_BIN=/path/to/adb npm start`. +Saved TVs persist in `./data/devices.json` on the host (bind-mounted into +the container), so they survive `docker compose down`/rebuilds. Override the +port by changing the `PORT` environment variable and the left side of the +`ports` mapping in `docker-compose.yml`. + +### Adding this as a service in another docker-compose stack + +If you're folding this into a larger compose project rather than running it +standalone, you have two straightforward options: + +1. **Vendor the source** (git submodule or subtree at, say, + `services/tv-adb-remote/`) and point that stack's compose file at it: + ```yaml + services: + tv-adb-remote: + build: ./services/tv-adb-remote + ports: + - "8787:8787" + volumes: + - ./services/tv-adb-remote/data:/app/data + restart: unless-stopped + ``` +2. **Build and push an image** (e.g. to GHCR or Docker Hub) from this repo's + CI, then reference it by tag instead of a build context — no vendored + source in the other repo at all: + ```yaml + services: + tv-adb-remote: + image: ghcr.io//tv-adb-remote:latest + ports: + - "8787:8787" + volumes: + - ./tv-adb-remote-data:/app/data + restart: unless-stopped + ``` + This fits a "docker nearly exclusively" stack best if most of its other + services are already referenced as prebuilt images rather than local + build contexts — ask if you want a GitHub Actions workflow added here to + publish the image on push. + +Either way, the only two things the other repo's compose file needs to get +right are the port mapping and a persistent volume for `/app/data`. ## Development ```sh cd server +npm install npm test ``` Runs the server's unit tests (command construction, input validation, -device persistence) using Node's built-in test runner — no real TV -required. +device persistence) using Node's built-in test runner — no real TV, and no +Docker, required. Useful for iterating on server code before rebuilding the +image. ## Project layout ``` +Dockerfile Bundles adb + the server into one image +docker-compose.yml Standalone run config (port 8787, data/ volume) server/ Express app + ADB bridge (source of truth for control logic) src/adb.js Thin wrapper around the `adb` CLI (connect/pair/keyevent/…) src/keycodes.js Android KeyEvent constants used by the remote buttons diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9674616 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +name: tv-adb-remote + +services: + tv-adb-remote: + build: . + image: tv-adb-remote + container_name: tv-adb-remote + hostname: tv-adb-remote + ports: + - "8787:8787" + volumes: + - ./data:/app/data + environment: + - PORT=8787 + restart: unless-stopped