Kiro 4b747c74ca feat: port missing MediaStation features (DLNA, STRM, files, dup, sched, api-configs, emby, storage)
Audit-driven port from the original Python MediaStation. Eight major
subsystems that were absent from the Go rewrite are now in place,
each with its own service, handler, frontend page and smoke-test
assertions.

Backend services
  - service/crypto.go: AES-256-GCM encrypt/decrypt for at-rest secrets
    keyed off the JWT secret. Legacy plaintext rows pass through
    unchanged for smooth upgrades. Unit-tested.
  - service/api_config.go: third-party provider config (TMDb, Bangumi,
    TheTVDB, Fanart, Douban, OpenAI). Seeds defaults on first run.
    Encrypts api_key on write, returns masked 'abc1****wxyz' projection.
  - service/duplicate.go: sparse-sample MD5 (head + middle + tail, 1MiB
    each, plus file-size suffix) duplicate finder. Picks 'best' primary
    (matched > size > id) and marks others is_duplicate=true.
  - service/filemanager.go: server-side allow-listed file browser used
    by the library-path picker. Strict path-traversal protection.
  - service/dlna.go: real SSDP M-SEARCH discovery + AVTransport
    SetAVTransportURI/Play SOAP cast. 30 s discovery cache.
  - service/scheduler.go: 3 recurring background jobs (library_scan
    60min, transcode_cleanup 24h, recycle_purge 24h with 30-day
    cutoff). Status + run-now endpoints.
  - service/cache_cleanup.go: walkAndPrune helper used by scheduler.
  - service/storage.go: DB-only disk-usage breakdown by library and by
    container format.
  - service/emby_compat.go: read-only Emby/Jellyfin shim
    (System/Info, Users, Users/x/Views, Items, PlaybackInfo) so Infuse
    / VidHub / Kodi can browse MediaStationGo libraries.

Model updates
  - Media: new strm_url (302 redirect target), file_hash, is_duplicate,
    duplicate_of fields.
  - APIConfig: new table for encrypted provider secrets.
  - AutoMigrate registers APIConfig.

Stream layer
  - StreamService.ServeFile now redirects 302 to strm_url when set so
    WebDAV / Alist / S3 / HTTP direct links work transparently.

Handlers + routes
  - Authed: GET /files, GET /storage, GET /dlna/devices, POST /dlna/cast,
    PUT/DELETE /media/:id/strm, POST /strm/import,
    POST /duplicates/{scan,unmark}.
  - Admin: GET/PUT/DELETE /admin/api-configs/:provider,
    GET /admin/scheduler, POST /admin/scheduler/:name/run.
  - New /emby/* group: System/Info, Users, Users/:userId/Views,
    Users/:userId/Items, Items/:id/PlaybackInfo (auth-required).

Frontend pages (lazy-loaded, 7 new chunks)
  - DlnaPage: device list + media picker + cast button.
  - FileManagerPage: root selector + breadcrumb + sortable listing.
  - APIConfigsPage: per-provider card with masked-key editor.
  - StoragePage: usage tiles + per-library bars + per-container grid.
  - DuplicatesPage: scan form + grouped report with primary highlight.
  - SchedulerPage: live job table with run-now button (5s refresh).
  - Sidebar reorganised: 自动化 group adds DLNA, 管理 group adds
    存储 / 文件浏览 / 重复文件 / 定时任务 / API 配置.

Smoke test additions (all admin-only)
  - api-configs seeded with 6 providers
  - api-config encrypted in db (sqlite3 enc:v1: prefix check)
  - storage breakdown
  - file browser lists library root + rejects /etc (path traversal)
  - dlna devices endpoint
  - scheduler exposes 3 jobs + run library_scan
  - emby /System/Info + /Users/{x}/Views
  - strm set + stream 302 + strm clear
  - duplicate scan

Verified: go build, go vet, go test (incl. new TestCrypto* suite + the
existing TestParseEpisode/TestCleanQuery/TestSrtToVTT/TestStripASSTags/
TestBuildFFmpegArgs); tsc -b && vite build emits 28 route chunks plus
the deferred hls chunk; main bundle 253 KB / 85 KB gzipped; smoke test
PASS=42 / FAIL=0.
2026-05-15 10:58:32 +00:00

🎬 MediaStationGo

A Go rewrite of MediaStation — your private home media center.

Go React TypeScript SQLite Docker License


Why a rewrite?

The original MediaStation is a Python/FastAPI + Vue project. MediaStationGo is a from-scratch reimplementation that adopts the lighter, single-binary deployment model used by cropflre/nowen-video:

  • Backend: Go 1.25 + Gin + GORM + SQLite (WAL).
  • Frontend: React 18 + Vite + Tailwind + Zustand.
  • Distribution: one ~30 MB static binary (CGO disabled), or a multi-arch Alpine Docker image.

The goal is to keep the user-facing feature surface familiar (libraries, scanning, scraping, direct play/HLS, multi-user, downloads, RSS) while making deployment painless on NAS hardware.


Features

Authentication & users

  • ✅ JWT auth with admin/user roles
  • ✅ First-run admin seeding (admin / admin123, override via ADMIN_INITIAL_PASSWORD)
  • ✅ Profile page (email / avatar / change password)
  • ✅ Admin user table with role promotion / demotion
  • ✅ Audit log written for sensitive actions (login, library CRUD, downloads, …)

Library management

  • ✅ Library CRUD + recursive filesystem scan
  • ✅ ffprobe metadata extraction (duration / resolution / codecs / container)
  • ✅ Scene-noise filename cleaner with year + season/episode parsing
  • ✅ Multi-provider scrape chain by library type:
    • movie → TMDb (with optional Fanart.tv high-res poster upgrade)
    • tv → TheTVDB (fallback TMDb)
    • anime → Bangumi (fallback TMDb)
  • ✅ Image proxy with disk cache for TMDb / Bangumi / Douban / Fanart / TheTVDB
  • ✅ TV / anime libraries grouped by season with episode listing
  • ✅ fsnotify-based filesystem watcher with 5 s coalescing debouncer

Playback

  • ✅ Direct-play streaming with HTTP Range support
  • ✅ HLS on-demand transcoding (single ffmpeg job per media)
  • ✅ External subtitle discovery (.srt / .vtt / .ass / .ssa) with on-the-fly WebVTT conversion
  • ✅ Resume position written every 10 s + Continue Watching row on home
  • ✅ Favourites (toggle) + ordered Playlists (CRUD)

Automation

  • ✅ qBittorrent download integration (add / list / delete via Web UI API)
  • ✅ RSS subscriptions with regex filters, GUID dedup and 10-minute polling

Operations

  • ✅ Real-time scan / scrape / transcode / download / subscription events over WebSocket
  • ✅ Operator dashboard at /stats (CPU / memory / disk / library counts / Goroutines)
  • ✅ Real-time tasks panel at /tasks (active ffmpeg jobs + qBittorrent torrents)
  • ✅ Recycle bin at /recycle (soft delete + restore + purge)
  • ✅ NFO export (Kodi / Jellyfin compatibility) — single media or whole library
  • ✅ Hardware-accel encoder profiles: software / NVENC / Intel QSV / VAAPI
  • ✅ Single-binary build, multi-arch Docker image, GitHub Actions CI + GHCR publish

Discovery & AI

  • ✅ TMDb Discover — Trending (today) + Popular rails on /discover
  • ✅ AI smart search (OpenAI-compatible) — natural-language queries → structured intent
  • ✅ AI recommendations seeded from your watch history (GET /api/ai/recommend)

Frontend

  • ✅ React SPA with code-splitting: Login / Home / Library / Search / Favourites / Playlists / Media detail / Player (HLS + direct + subtitles) / Profile / Downloads / Subscriptions / Stats / Admin
  • ✅ Global toast notifications driven by the WebSocket hub
  • ✅ Initial bundle ~250 KB / 83 KB gzipped (hls.js loaded only on first HLS playback)

Roadmap

Area Status
Bidirectional Jellyfin / Emby compatibility layer ⏳
DLNA / Chromecast ⏳
Online subtitle search providers ⏳
Multi-bitrate ABR transcode profiles ⏳

Quick start

Docker

git clone https://github.com/ShukeBta/MediaStationGo.git
cd MediaStationGo

# (optional) edit docker-compose.yml to mount your media root at /media
docker compose up -d

Open http://localhost:8080 and log in with admin / admin123.

Bare metal

# requirements: Go 1.25+, Node 20+, ffmpeg
make build       # produces bin/mediastation-go and web/dist
./bin/mediastation-go

Local development

make dev         # backend on :8080, MEDIASTATION_APP_DEBUG=true
make dev-web     # vite dev server on :3000, proxies /api -> :8080

Configuration

Configuration is layered — defaults < config.yaml < config/*.yaml < environment variables prefixed with MEDIASTATION_.

Most-used keys

Key Default Purpose
MEDIASTATION_APP_PORT 8080 HTTP listen port
MEDIASTATION_APP_DATA_DIR ./data DB / cache / JWT secret root
MEDIASTATION_APP_WEB_DIR ./web/dist SPA bundle to serve
MEDIASTATION_DATABASE_DB_PATH ./data/mediastation.db SQLite file
MEDIASTATION_SECRETS_JWT_SECRET (auto) JWT signing key
MEDIASTATION_SECRETS_TMDB_API_KEY (empty) Enables movie scraping
MEDIASTATION_SECRETS_BANGUMI_ACCESS_TOKEN (empty) Optional, raises Bangumi rate limit
MEDIASTATION_APP_CORS_ORIGINS (empty) Allow-list, JSON array
ADMIN_INITIAL_PASSWORD admin123 Bootstrap admin password

Runtime settings (admin → 设置)

These live in the settings table and can be edited from the admin UI:

Key Purpose
qbittorrent.url qBittorrent Web UI base URL
qbittorrent.username qBittorrent user
qbittorrent.password qBittorrent password
qbittorrent.savepath Optional default save path for new torrents

After editing, hit 下载 → 重新加载配置 (or POST /api/downloads/reload) so the qBittorrent client picks up the new credentials.

See config.example.yaml for the full surface.


Project layout

MediaStationGo/
├── cmd/server/main.go          Application entry point
├── internal/
│   ├── config/                 Viper-based config loader
│   ├── database/               GORM + SQLite (WAL) bootstrap
│   ├── model/                  GORM data models + AutoMigrate registry
│   ├── repository/             Thin data-access layer
│   ├── service/                Business logic
│   │   ├── auth.go             login / register / JWT / seed admin
│   │   ├── media.go            library + media CRUD
│   │   ├── scanner.go          fs walker + ffprobe + scrape kick
│   │   ├── ffprobe.go          ffprobe wrapper
│   │   ├── tmdb.go             TMDb provider
│   │   ├── bangumi.go          Bangumi provider
│   │   ├── scraper.go          orchestrator + filename cleaner
│   │   ├── stream.go           direct play + HLS playlist / segment
│   │   ├── transcoder.go       per-media ffmpeg HLS job manager
│   │   ├── subtitle.go         external subtitle discovery + .vtt conversion
│   │   ├── image_proxy.go      cached, allow-listed image proxy
│   │   ├── playback.go         history / favourites / playlists
│   │   ├── watcher.go          fsnotify debouncer
│   │   ├── qbittorrent.go      qBittorrent v2 API client
│   │   ├── downloads.go        download orchestrator + WS poller
│   │   ├── subscription.go     RSS poller
│   │   ├── stats.go            dashboard snapshot
│   │   ├── profile.go          non-credential user mutations
│   │   ├── audit.go            audit log writer
│   │   ├── ws_hub.go           pub/sub broker for the WS
│   │   └── walk.go / episode_parser.go  helpers
│   ├── middleware/             Gin middleware (CORS / JWT / admin)
│   └── handler/                HTTP route definitions (one file per concern)
├── web/                        React 18 + Vite SPA
│   ├── src/api/                axios helpers (one per service)
│   ├── src/components/         Layout, MediaCard, GlobalEvents, RequireAuth
│   ├── src/hooks/              useWebSocket, …
│   ├── src/pages/              Home / Library / Search / Player / Downloads / …
│   ├── src/stores/             Zustand (auth)
│   └── src/types/              Domain types mirrored from Go
├── Dockerfile                  Multi-stage, multi-arch build
├── docker-compose.yml          NAS-friendly deployment
├── Makefile                    build / dev / docker / test
├── config.example.yaml         Full configuration template
└── .github/workflows/          CI + GHCR publish

License

Released under the GNU GPL v3.0.

S
Description
No description provided
Readme 76 MiB
Languages
Go 74.3%
TypeScript 25.6%