feat: backup, notifier, organizer, douban, watch-history, poster-wall

Complete the port of the remaining MediaStation features:

Backend services:
  - service/backup.go: SQLite hot backup via VACUUM INTO, list, delete,
    restore. Backup files stored under {data_dir}/backups/ with timestamps.
  - service/notifier.go: Multi-channel push notifications (Telegram /
    Bark / WeChat Server酱 / Webhook). Configuration via settings table
    keys notify.telegram.*, notify.bark.*, etc.
  - service/organizer.go: Auto-rename + move media files into library
    directory structure. Movie: {Title} ({Year})/{Title} ({Year}).ext,
    TV: {Title}/Season XX/{Title} - SxxExx.ext. Both per-media and
    per-library batch endpoints.
  - service/douban.go: Douban (豆瓣) metadata provider using the
    unofficial subject_suggest API. Returns Chinese titles + posters.
    Requires douban_cookie in secrets config.

Handlers:
  - handler/backup.go: GET/POST/DELETE /admin/backups, POST restore.
  - handler/organizer.go: POST /admin/media/:id/organize,
    POST /admin/libraries/:id/organize.
  - handler/notify.go: POST /admin/notify/test.

Frontend pages:
  - WatchHistoryPage.tsx: full paginated history with progress bars,
    resume buttons, poster thumbnails and timestamps.
  - PosterWallPage.tsx: dense 8-column poster grid of all media
    (matches the original MediaStation PosterWallView.vue).

Sidebar additions: 观看历史 + 海报墙 links in the main nav group.

Verified: go build, go vet, go test all pass; tsc -b && vite build
emits 30 lazy chunks; main bundle 254 KB / 85 KB gzipped.
This commit is contained in:
Kiro
2026-05-15 14:17:26 +00:00
parent 4b747c74ca
commit 778bf77437
13 changed files with 864 additions and 0 deletions
+12
View File
@@ -51,6 +51,10 @@ type Container struct {
Scheduler *SchedulerService
Storage *StorageService
Emby *EmbyService
Backup *BackupService
Notifier *NotifierService
Organizer *OrganizerService
Douban *DoubanProvider
stopCtx context.Context
stopCancel context.CancelFunc
@@ -82,6 +86,10 @@ func New(cfg *config.Config, log *zap.Logger, repos *repository.Container) *Cont
dlna := NewDLNAService(log)
storage := NewStorageService(log, repos)
emby := NewEmbyService(cfg, log, repos)
backup := NewBackupService(cfg, log, repos.DB)
notifier := NewNotifierService(log, repos)
organizer := NewOrganizerService(cfg, log, repos)
douban := NewDoubanProvider(cfg, log)
scheduler := NewSchedulerService(log, repos, scanner, transcoder, hub, cfg.Cache.CacheDir)
ctx, cancel := context.WithCancel(context.Background())
@@ -122,6 +130,10 @@ func New(cfg *config.Config, log *zap.Logger, repos *repository.Container) *Cont
Scheduler: scheduler,
Storage: storage,
Emby: emby,
Backup: backup,
Notifier: notifier,
Organizer: organizer,
Douban: douban,
stopCtx: ctx,
stopCancel: cancel,
}