npm package: @mangod33/syncgo
Description: SyncGo is a single-process, single-port web app that syncs or transfers tracks from a **public** Spotify playlist into Navidrome (Subsonic API). The backend (Fastify) and frontend (React + Vite) run together on `localhost:3000` by default.
Latest version: 1.0.0

# SyncGo

SyncGo is a single-process, single-port web app that syncs or transfers tracks from a **public** Spotify playlist into Navidrome (Subsonic API). The backend (Fastify) and frontend (React + Vite) run together on `localhost:3000` by default.

## Requirements

- Node.js 18+
- pnpm
- Navidrome with Subsonic API enabled
- Spotify API Client Credentials (public playlists only)

## Setup

```bash
pnpm install
cp .env.example .env
```

Fill in `.env` with your Spotify app credentials (optional), Navidrome URL (optional), and database path.

```bash
SPOTIFY_CLIENT_ID=...
SPOTIFY_CLIENT_SECRET=...
```

If you do not set Spotify credentials in `.env`, SyncGo will start and prompt for them after Navidrome login. The credentials entered in the UI are used only for the current session and are not saved.
If you set `NAVIDROME_URL` in `.env`, the login screen will auto-fill it (unless you already have a saved cookie), and you can also tap the link icon to apply the default.

### Environment

- `PORT` - server port (default `3000`)
- `NODE_ENV` - `development` or `production`
- `NAVIDROME_URL` - optional; default Navidrome URL for the login screen
- `DATABASE_URL` - SQLite file path (default `./data/syncgo.db`)
- `SPOTIFY_CLIENT_ID` / `SPOTIFY_CLIENT_SECRET` - optional; if omitted, the UI will ask per session

## Development (single port)

```bash
pnpm dev
```

Fastify starts on `http://localhost:3000` and serves the Vite frontend through middleware.

## Production (single port)

```bash
pnpm build
NODE_ENV=production pnpm start
```

Vite builds the frontend to `dist/client` and Fastify serves both the API and static UI from the same port.

## Hosting on a domain

SyncGo is a single-port app, so you can run it on an internal port and put a reverse proxy in front of it for TLS.

1) Point your domain to the server (DNS A/AAAA record).
2) Open ports 80/443 on the server firewall.
3) Run SyncGo on an internal port (for example `3000`) with `NODE_ENV=production`.
4) Put a reverse proxy in front of it to terminate TLS and forward traffic.

`NODE_ENV=production` enables secure cookies, so be sure to serve SyncGo behind HTTPS in production.

### Example: Caddy (recommended for automatic TLS)

```
yourdomain.com {
  reverse_proxy 127.0.0.1:3000
}
```

### Example: Nginx

```
server {
  listen 80;
  server_name yourdomain.com;

  location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}
```

Then enable TLS (for example with Certbot) and reload Nginx.

### Service tips

- Run SyncGo under a process manager (systemd, pm2) so it stays up.
- Keep `DATABASE_URL` on persistent storage.
- Protect the SQLite file; it stores active Navidrome session credentials.
- If you deploy to a public server, always use HTTPS.

## User flow (required order)

1. Navidrome login (URL, username, password)
2. Choose mode (sync or transfer)
3. Validate Spotify playlist (URL or ID)
4. Run job and review results / export failures

## Features

- **Sync / transfer modes**: Sync adds missing matches; transfer creates a new Navidrome playlist.
- **Dry run + apply**: Dry runs do not create or modify playlists. You can apply changes after review.
- **Run history**: Session-only run history and summaries (no extra storage).
- **Manual match**: Open a failed track, search Navidrome, and add/remove a match manually.
- **Preview**: Spotify embed preview for the selected Spotify track and Navidrome stream previews for search results (requires Subsonic streaming enabled).
- **Match tuning**: Adjust fuzzy match threshold and exact duration tolerance per run.

## Common troubleshooting

- **INVALID_NAVIDROME_CREDENTIALS**: the username/password is wrong or Subsonic API is disabled.
- **NAVIDROME_UNREACHABLE**: the URL is invalid, unreachable, or the server did not return JSON.
- **SPOTIFY_NOT_CONFIGURED**: `SPOTIFY_CLIENT_ID`/`SPOTIFY_CLIENT_SECRET` missing.
- **SPOTIFY_PRIVATE_OR_UNAVAILABLE**: playlist is not public or cannot be accessed with client credentials.

## Notes

- Sync mode never removes tracks; it only adds missing matches and can update the playlist public/private flag when toggled.
- Transfer mode creates a new Navidrome playlist.
- Matching order: ISRC → exact (title/artist + duration within tolerance; default 25s, adjustable 0–60s) → fuzzy (weighted title/artist; default threshold 82%).
- Failed matches can be exported from the results panel as JSON or CSV.