# MyVideos

MyVideos is now a single-process Go desktop application with an embedded web UI.
The app starts the local server in-process, opens the browser, and can optionally run with tray support.

## Repository layout

- `center/cmd/myvideos-center`: public center server entrypoint for edge heartbeats + tunnel proxy
- `center/internal/...`: center config, HTTP API, edge registry, and tunnel session hub
- `server/cmd/myvideos`: desktop app entrypoint (single process)
- `server/internal/myvideosserver`: embeddable server package (internal to `server` module)
- `server/cmd/myvideos-server-cli`: standalone server CLI entrypoint
- `server/internal/autostart`: autostart management (Linux support + fallback stubs)
- `server/internal/runtimeenv`: runtime environment setup for bundled Python/model paths
- `server/internal/tray`: optional tray integration (build tag: `tray`)
- `scripts/`: build and packaging scripts

## Requirements

- Go `1.26+`
- Linux packaging tools when building `.deb`/`.AppImage`:
  `dpkg-deb`, `curl`, `python3`, and optionally `file`
- Tray profile build requirements (Linux):
  `pkg-config`, `gtk+-3.0` dev files, and `ayatana-appindicator3-0.1` or `appindicator3-0.1` dev files
- Tray profile build requirements (macOS):
  Xcode Command Line Tools (`clang`, `xcrun`, `lipo`)
- Tray profile build requirements (Windows):
  Go toolchain (CGO enabled) on `windows/amd64` host
- macOS signing/notarization for public distribution:
  Apple Developer Program membership + `Developer ID Application` certificate

## Run from source

Run desktop app (single process):

```bash
SERVER_LDFLAGS="$(./scripts/server_build_ldflags.sh)"
cd server
go run -ldflags "$SERVER_LDFLAGS" ./cmd/myvideos --host=127.0.0.1 --port=51345
```

Run server only:

```bash
SERVER_LDFLAGS="$(./scripts/server_build_ldflags.sh)"
cd server
go run -ldflags "$SERVER_LDFLAGS" ./cmd/myvideos-server-cli --host=127.0.0.1 --port=51345
```

Run center server:

```bash
cd center
go run ./cmd/myvideos-center --host=0.0.0.0 --port=61345
```

## Build commands

Build bundled server binaries:

```bash
./scripts/build_server.sh
```

This script writes platform binaries to `assets/server/` as local build artifacts
that are intentionally not tracked in Git. Server/desktop builds embed the root
`VERSION` value into Go variable `MYVIDEOS_SERVER__VERSION`.

Build desktop binaries:

```bash
./scripts/build_desktop_go.sh --profile notray
./scripts/build_desktop_go.sh --profile tray
```

Build center binaries:

```bash
./scripts/build_center.sh
```

`--profile tray` host behavior:

- Tray builds use CGO and are not cross-platform release builds. Build the Windows tray binary on Windows amd64 and the macOS tray binaries on macOS.
- Linux host: builds native tray binary only (`linux/amd64` or `linux/arm64`)
- macOS arm64 host: builds native tray binary only (`darwin/arm64`)
- macOS x64 host: builds `darwin/amd64`, `darwin/arm64`, and `darwin/universal2`
- Windows x64 host: builds native tray binary only (`windows/amd64`) with GUI subsystem (no console window)

Disable universal2 generation on macOS x64 host with:

```bash
DESKTOP_MACOS_UNIVERSAL2=0 ./scripts/build_desktop_go.sh --profile tray
```

Package desktop binary archives:

```bash
./scripts/package_desktop_go_archives.sh --profile notray
./scripts/package_desktop_go_archives.sh --profile tray
./scripts/package_desktop_go_archives.sh --public-set
```

- `--public-set` forces the standard public download set and verifies these four archives exist: `windows-x64`, `macos-x64`, `macos-arm64`, `linux-x64`. It also writes matching `.sha256` files and refreshes `shared/versioninfo/releases.json` from git semver tags plus the current public desktop artifacts.

Package a macOS `.app` bundle for Gatekeeper-friendly distribution:

```bash
./scripts/package_macos_app.sh --platform macos-arm64
MYVIDEOS_MACOS_SIGN_IDENTITY="Developer ID Application: Example Co, Ltd. (TEAMID1234)" \
./scripts/package_macos_app.sh --platform macos-arm64
MYVIDEOS_MACOS_SIGN_IDENTITY="Developer ID Application: Example Co, Ltd. (TEAMID1234)" \
MYVIDEOS_MACOS_NOTARY_PROFILE="myvideos-notary" \
./scripts/package_macos_app.sh --platform macos-arm64 --notarize
```

- The script assembles `MyVideos.app`, optionally signs it with `codesign`, optionally submits it through `xcrun notarytool`, and writes a distributable archive plus `.sha256`.
- Full setup notes: `docs/macos-codesign-notarization.md`

Create an annotated semver release tag from root `VERSION`:

```bash
./scripts/create_release_tag.sh
./scripts/create_release_tag.sh --push
```

- Tag format is `v<VERSION>`.
- The helper refuses to tag a dirty worktree by default and checks both local and remote tag collisions before creating the tag.
- After tagging, rerun `python3 ./scripts/generate_release_manifest.py` or `./scripts/package_desktop_go_archives.sh --public-set` so published release history picks up the new tag metadata.

Upload the public desktop download archives used by `wdsp.top`:

```bash
./scripts/upload_desktop_go_downloads.sh --dry-run
./scripts/upload_desktop_go_downloads.sh --verify-public
```

- Public download set defaults to 4 files: `windows-x64`, `macos-x64`, `macos-arm64`, `linux-x64`.
- Upload path is versioned as `/downloads/myvideos/desktop-go/v<VERSION>/...`
- The upload helper also refreshes remote `releases.json` / `latest.json` indexes and updates flat latest-file aliases for backward compatibility.
- macOS uploads can point to signed/notarized `.app` archives by overriding `--macos-x64-file` / `--macos-arm64-file`; the manifest generator will publish those filenames and hashes.

Build Linux distributables:

```bash
./scripts/package_linux.sh --profile core --desktop-profile notray
./scripts/package_linux.sh --profile lite --desktop-profile notray
```

- `core` packages only the desktop app.
- `lite` packages the desktop app plus the bundled shared AI runtime.
- Formal release gate checklist: `RELEASE_CHECKLIST.md`

Use tray-enabled Linux packaging:

```bash
./scripts/package_linux.sh --profile core --desktop-profile tray
```

- Linux `tray` packages now ship both `myvideos-tray` and a non-tray fallback binary.
  Launching `./myvideos` auto-falls back to the non-tray binary when the host is
  missing tray runtime libraries such as `libayatana-appindicator3.so.1`.
- To skip tray explicitly on Linux even when the tray binary is runnable, use
  `MYVIDEOS_FORCE_NO_TRAY=1 ./myvideos` or pass `--disable-tray`.

## Runtime data

- Default data dir uses Go `os.UserConfigDir()` plus `myvideos`
  - Linux: `$XDG_CONFIG_HOME/myvideos`, otherwise `~/.config/myvideos`
  - macOS: `~/Library/Application Support/myvideos`
  - Windows: `%AppData%\myvideos`
- Override with: `MYVIDEOS_DATA_DIR=/custom/path`
- Version source for packaging and embedded server build version: root `VERSION` file
- Center public release catalog fallback source: `shared/versioninfo/releases.json` (newest entry should match root `VERSION`)
- Release manifest generator: `scripts/generate_release_manifest.py`

## Center / edge sync knobs

- Edge (`myvideos-server`) -> center:
  - Center URL is fixed in server code: `https://wdsp.top`
  - Edge `nodeId` is auto-generated on first start and persisted at `<data-dir>/center-node-id.txt`
  - Heartbeat interval is fixed in server code: `30s`
  - HTTP timeout is fixed in server code: `10s`
  - `MYVIDEOS_SERVER__VERSION` is linker-injected from root `VERSION` during build/package; not read from runtime env
  - Center records edge version history from tunnel heartbeats and exposes it to admin queries/UI
  - Edge version history retention is built into center: merge by `nodeId + version + startedAt`, keep about the last 180 days, and prune to the newest 200 rows per node during a best-effort cleanup pass about every 6 hours
  - Tunnel auth token source is the embedded shared token from `shared/sharedtoken/default_center_shared_token.txt`; edge tunnel starts at server startup and is no longer tied to per-user login/logout
- Center (`myvideos-center`) runtime:
  - `MYVIDEOS_CENTER_SHARED_TOKEN=<shared-token>` (defaults to the embedded shared token; empty disables auth)
  - `MYVIDEOS_CENTER_HOST=0.0.0.0`
  - `MYVIDEOS_CENTER_PORT=61345`
  - `MYVIDEOS_CENTER_RELEASE_CATALOG_URL=https://wdsp.top/downloads/myvideos/desktop-go/releases.json` (`disabled` / `off` / `none` disables runtime refresh and keeps embedded-only fallback)
  - `MYVIDEOS_CENTER_RELEASE_CATALOG_TIMEOUT_SECONDS=5`
  - `MYVIDEOS_CENTER_RELEASE_CATALOG_REFRESH_SECONDS=30`
  - `MYVIDEOS_CENTER_HEARTBEAT_TTL_SECONDS=120`

## Edge tunnel routes

- Public release metadata:
  - `GET /api/version/latest`
  - `GET /api/version/history`
- Public desktop release indexes:
  - `/downloads/myvideos/desktop-go/releases.json`
  - `/downloads/myvideos/desktop-go/latest.json`
- Edge -> center tunnel connect (edge client uses this internally):
  - `GET /api/edges/tunnel/connect?nodeId=<edge-node-id>` (WebSocket + Bearer token)
- Public edge proxy entry (external user traffic):
  - `/e/<nodeId>/...` forwards to the corresponding online edge `myvideos-server`
- Admin version history query:
  - `GET /api/admin/edges/version-history?nodeId=<edge-node-id>&limit=<n>` (admin browser session required)

## E2E tunnel check

- `./scripts/e2e_center_tunnel_login.sh`
  - Starts isolated center/server processes and verifies:
    - shared edge tunnel comes online before any user login
    - user login succeeds while the shared tunnel stays online
    - user logout does not tear down the shared tunnel
  - Requires reachable MySQL for center startup (override with `E2E_CENTER_MYSQL_DSN=...`)

## Autostart CLI

The desktop app supports:

```bash
cd server
go run ./cmd/myvideos --autostart-status
go run ./cmd/myvideos --autostart-enable
go run ./cmd/myvideos --autostart-disable
```

## Tests

```bash
cd server
go test ./...
```
