984 Commits

Author SHA1 Message Date
ShukeBta 7fa99a5669 fix: 使 TMDbProvider 支持从数据库读取 API Key
- 修改 TMDbProvider 添加 apiConfig *APIConfigService 字段
- NewTMDbProvider 接受 apiConfig 参数(可为 nil)
- 添加 resolveAPIKey(ctx) 方法:优先从 config 读取,回退到数据库
- 添加 resolveBaseURL(ctx) 方法:解析基础 URL
- 修改 SearchMovie 使用 resolveAPIKey / resolveBaseURL
- 修改 DiscoverService.Fetch 使用 resolveAPIKey / resolveBaseURL
- 调整 service.go 初始化顺序,确保 tmdb 在 scraper 之前初始化
- 构建验证通过
2026-05-18 01:57:48 +08:00
ShukeBta cdd28b8b88 feat: 添加 FlareSolverr 配置支持以绕过 Cloudflare/WAF
- 在 config.example.yaml 中添加 flaresolverr 配置段
- 在 internal/config/config.go 中添加 FlareSolverrConfig 结构体
- 修改 SiteService 接收 flaresolverr URL
- 更新 TestConnection 使用配置的 FlareSolverr URL
- 构建验证通过
2026-05-18 01:46:48 +08:00
ShukeBta a8e825ec13 fix: 增强站点连接测试HTTP请求头,添加浏览器仿真与FlareSolverr支持
- 新增 internal/helper/http.go:带浏览器仿真请求头和Cloudflare/WAF绕过支持
- TestSiteConnectivity 支持 FlareSolverr 代理(需配置 flareSolverrUrl)
- 检测 Cloudflare challenge 页面并返回友好错误信息
- 重构 service/site.go TestConnection 使用新的 helper 方法
2026-05-18 01:41:00 +08:00
ShukeBta 630f925725 fix: 修复站点管理API Key保存问题,移动RSS字段到主表单,修复Discover页面提示 2026-05-18 01:22:11 +08:00
ShukeBta 9827b46c49 feat(site): 补全 Site 模型 11 个缺失字段并同步前端类型
- model/site.go: 新增 user_agent/rss_url/timeout/priority/use_proxy/rate_limit/
  browser_emulation/login_status/upload_bytes/download_bytes/downloader 11个字段
- 字段对齐 Python 原版 MediaStation Site 模型(28字段 → 23字段)
- GORM AutoMigrate 自动补列,旧数据填充默认值(timeout=15, priority=50)
- types/index.ts: Site 接口同步所有新字段(带注释)
- 后端编译 ✅  前端构建 ✅  API 验证 ✅
2026-05-18 00:52:11 +08:00
ShukeBta 828ca36d7f @
feat: Warm Industrial UI redesign + social footer + code deduplication

Design System (Phase A):
- New palette: warm charcoal #111110 / amber gold #c9954a / sage green #7a9a8a
- Fonts: Cabinet Grotesk (heading) + Geist (body) + JetBrains Mono (data)
- SVG grain texture overlay, collapsed sidebar with hover expand
- Bento grid dashboard with WideContinueCard + PosterCard + QuickLink
- Backward-compatible CSS aliases for glass-panel/neon-button/input-base

Social Footer (Phase B):
- AppFooter component with GitHub repo / author homepage / TG group links
- Embedded in LoginPage, Layout (global), and SettingsPage
- TG group badge added to README.md

Code Cleanup:
- Removed deprecated DownloadClientCard / NotifyChannelCard components
- Merged downloadClient/notify API clients into unified modules
- Route deduplication: removed authed-group duplicates for admin endpoints
- Removed redundant AdminPage tabs (sites/downloads/notify/scheduler)
- UI fixes: blank pages (discover/download-clients/notify-channels) nil slice defense
- Email channel support in NotifyChannelsPage
@
2026-05-17 23:13:26 +08:00
ShukeBta 9be81222cc fix: 修复三个空白页面 + 许可证页面重构
- 修复发现页空白:后端 discover.go nil→[] Match,前端 discover.ts 加 ?? []
- 修复下载客户端页空白:后端 download_clients.go nil→[]model.DownloadClient,前端加 ?? []
- 修复通知渠道页空白:后端 notify_channels.go nil→[]struct{},前端加 ?? []
- 许可证页面重构:移除生成密钥功能,对接 LicenseServer 绑定/状态模式
- 所有变更通过 Go build + npm run build 零错误验证
2026-05-17 00:54:40 +08:00
ShukeBta 158fffaba0 feat: add public IP detection for VPS/NAS deployment
- Add getPublicIP() via api.ipify.org with configurable timeout
- Startup log now shows both local and public access URLs
- Graceful fallback: skip public IP if network unreachable
- Update README with dual-IP log example for both CN/EN
2026-05-17 00:19:38 +08:00
ShukeBta dd8f2a466c feat: auto-detect local IP on startup, update README access URL
- Add getLocalIP() to detect first non-loopback IPv4 address
- Startup log now shows accessible URL (e.g. http://192.168.1.4:8080)
- README: replace hardcoded localhost with auto-detected IP description
- Fallback to localhost if no network interface found
2026-05-17 00:17:00 +08:00
ShukeBta d10bc19e8e docs: premium README redesign with visual hierarchy
- Add centered header badge with light/dark theme support
- Add table of contents with emoji-anchored quick links
- Rewrite feature section as collapsible detail blocks
- Add Python vs Go comparison table with metrics
- Add prerequisite requirements table
- Enhance project structure with arrow annotations
- Add contribution section and footer
- Mirror all changes to README_EN.md
- 341 lines each, bilingual parity
2026-05-17 00:10:49 +08:00
ShukeBta 8e06b7e149 chore: remove license code, fix compilation, update README bilingual
- Remove all license-related code (handler/service/repository/model)
  License authorization is managed by separate server:
  https://github.com/ShukeBta/MediaStationLicenseServer
- Fix compilation errors: model field alignment, method name fixes,
  route conflicts, struct literal corrections (7 files)
- Add Chinese README.md as primary, English README_EN.md
- Update .gitignore: exclude .workbuddy/, editor backups
- Add new repository files: assistant, play_profile, storage_config
2026-05-17 00:02:41 +08:00
ShukeBta 37dd83c56d Resolve merge conflicts: merge all Go backend and React frontend changes 2026-05-16 22:48:30 +08:00
ShukeBta cbb4b806be feat: merge conflict resolution, site management, UI fixes 2026-05-16 17:57:34 +08:00
Shuke 4a319b4378 Merge pull request #8 from ShukeBta/feat/port-vue-ui-features
feat(web): port AI Assistant, STRM management, and admin tools pages from Vue UI
2026-05-16 17:55:42 +08:00
Kiro Agent 1dbd73b30b feat: complete Vue UI parity (full backend + frontend)
== New domain models (7) ==
- UserPermission: per-user feature toggles (13 booleans)
- StorageConfig: encrypted Alist/S3/WebDAV adapters
- LicenseKey + LicenseActivation: offline license issuance
- DownloadClient: multi-client downloader configs
- AssistantSession + AssistantMessage: multi-turn AI chat persistence

== New services (5) ==
- PermissionService: admin grants always-true; user defaults seeded
- StorageConfigService: AES-GCM encryption + per-type connection probe
- LicenseService: 24-char hyphenated key generation, activation/heartbeat
- DownloadClientService: qB/Aria2/Transmission CRUD + WebUI test
- AssistantService: chat history + execute/undo stubs (op_id tracking)

== Extended AIService.Chat ==
- Multi-turn LLM call with chat history; offline fallback reply

== New endpoints (60+) ==
Auth:
  POST /auth/refresh, /auth/logout, /auth/change-password
  PATCH /auth/profile
  GET /auth/permissions, /auth/me

Permissions admin:
  GET/PUT /admin/users/:id/permissions
  POST /admin/users/:id/permissions/reset

Search:
  GET /search, /search/advanced, /search/tmdb, /search/sites

System:
  GET /system/config, /settings/schema, /system/events/ticket
  POST /admin/system/scheduler/:name/trigger

Stats:
  GET /stats/user/:id, /stats/top-users
  POST /stats/play

Sites:
  GET /sites/:id/resource, /sites/:id/userdata

Subscriptions:
  PUT /subscriptions/:id, POST /subscriptions/:id/search

Playlists:
  POST /playlists/:id/reorder
  DELETE /playlists/:id/items/by-id/:item_id

DLNA per-renderer:
  POST /dlna/:uuid/{play,pause,stop}, GET /dlna/:uuid/status

Media:
  POST/DELETE /media/:id/favorite, GET /media/:id/favorite/status
  POST /media/:id/ai-scrape, /media/scrape/test, /media/organize
  GET /favorites (alias)

Playback:
  GET /playback/:id/info, /playback/:id/external-players, /playback/:id/external-url
  POST /playback/:id/progress
  GET /playback/transcode/:job_id/status

Downloads:
  POST /download/:id/{pause,resume,organize}
  POST /download/{organize,sync,start-auto-sync}
  GET /download/tasks
  Admin: full CRUD on /admin/download/clients + /admin/download/aria2/stats

License:
  POST /license/{activate,heartbeat}
  GET /license/{status,heartbeat-status}
  Admin: /admin/license/{generate,list,:id/activations,:id/revoke,activation/:id/unbind}

Storage:
  GET /admin/storage/{status,:type}
  PUT /admin/storage/:type, POST /admin/storage/:type/test

Assistant (multi-turn AI):
  GET/POST /admin/assistant/sessions
  GET/DELETE /admin/assistant/session/:id
  POST /admin/assistant/{chat,execute}
  POST /admin/assistant/undo/:op_id
  GET /admin/assistant/history

== New React pages (4) ==
- AssistantChatPage (/assistant): full multi-turn chat UI with sessions
  sidebar, optimistic user-turn append, live AI response.
- DownloadClientsPage (/download-clients): typed CRUD form for
  qBittorrent / Aria2 / Transmission + per-row Test action.
- LicensePage (/license): generate keys, list activations, revoke,
  unbind individual devices.
- StorageConfigPage (/storage-config): tabbed Alist/WebDAV/S3 form
  with secret-aware redaction + connection probe.

== New API helpers (5) ==
- assistant, download_clients, license, permissions, storage_config

== Layout ==
- Sidebar gains 4 new admin links (AI 对话, 下载器, 外部存储, 许可证).
2026-05-16 09:49:35 +00:00
Kiro Agent 7095d9ebec feat: port remaining Vue UI surfaces with full backend support
Backend additions:
- New GORM models: NotifyChannel, PlayProfile (with PIN hashing).
- NotifyChannelService: multi-channel CRUD + unified dispatcher
  supporting Telegram / Bark / WeChat / Webhook with optional
  per-channel event filtering.
- PlayProfileService: per-user 'viewing personas' with
  content-rating gates, library scoping, PIN protection, and
  player defaults (autoplay, skip-intro, audio/subtitle prefs).
- New endpoints:
    GET    /admin/notify/channels           list
    POST   /admin/notify/channels           create
    PUT    /admin/notify/channels/:id       update
    DELETE /admin/notify/channels/:id       delete
    POST   /admin/notify/channels/:id/test  send test
    GET    /play-profiles                   list (admin: ?all=true)
    POST   /play-profiles                   create
    PUT    /play-profiles/:id               update
    DELETE /play-profiles/:id               delete
    GET    /media/recent                    home page rail
    GET    /media/stats                     library composition
    GET    /watch-history                   list
    GET    /watch-history/stats             aggregate
    GET    /watch-history/continue          continue-watching rail
    DELETE /watch-history[?media_id=]       clear
    DELETE /watch-history/:id               remove one
    GET    /discover/sections               available rails
    GET    /discover/feed?sections=a,b      multi-section TMDb fetch
    GET    /system/info                     name/version/runtime
    GET    /system/status                   uptime/cpu/mem/disk
    GET    /system/scheduler                read-only scheduler view
    GET    /stats/overview                  counts only
    GET    /stats/trend                     daily plays
    GET    /stats/top-content               most played
    GET    /stats/libraries                 per-library size
    GET    /stats/monitor                   live hardware

Frontend additions:
- New API helpers: notify_channels, play_profiles, history,
  stats_extra, media_extra, discover_extra, system.
- ProfileManagementPage (/play-profiles): full CRUD form with
  PIN management, library scoping, language preferences.
- NotifyChannelsPage (/notify-channels, admin): typed config
  forms per channel type + per-channel test action.
- SettingsPage (/settings, admin): grouped key/value editor
  covering General, Organize/Scrape, Adult, qBittorrent.
- Layout sidebar links and App.tsx routes wired for all three.
2026-05-16 09:01:09 +00:00
Kiro Agent 261d342fa1 feat(web): port AI Assistant, STRM management, and admin tools pages from Vue UI
- AIAssistantPage (/ai): smart natural-language search showing parsed intent
  + matching media grid, plus history-based recommendations.
- StrmPage (/strm, admin): import streaming-only entries from URLs and
  attach/detach STRM URL on existing media.
- ToolsPage (/tools, admin): organize an entire library or single media
  item, send test notifications through configured channels.
- Wire new routes in App.tsx and add sidebar links in Layout.tsx.

Co-authored-by: Shuke <272197458+ShukeBta@users.noreply.github.com>
2026-05-16 08:41:43 +00:00
Shuke 108074feb0 Merge pull request #7 from ShukeBta/kiro/iter7-complete-port
feat: site management + cross-site torrent search
2026-05-16 16:34:01 +08:00
Kiro 7a18eb670a feat: site management + cross-site torrent search
Port the complete site management system from the original MediaStation:

Model:
  - model/model.go: Added Site table with fields for name, base_url,
    site_type (nexusphp/gazelle/unit3d/mteam/custom_rss), auth_type
    (cookie/api_key/authorization), cookie, api_key, auth_header,
    user_agent, rss_url, timeout, priority, use_proxy, enabled,
    login_status, downloader. Registered in AllModels().

Backend services:
  - service/site.go: SiteService with CRUD (Create/List/FindByID/Update/
    Delete), TestConnection (validates credentials via HTTP, updates
    login_status in DB), and Search (cross-site fan-out that queries
    every enabled site adapter and returns merged results sorted by
    seeders descending).
  - service/site_adapter.go: SiteAdapter interface + NewSiteAdapter
    factory + 5 implementations:
    * nexusPhpAdapter — regex HTML scraping of torrents.php
    * gazelleAdapter — JSON API /ajax.php?action=browse
    * unit3dAdapter — REST API /api/torrents/filter
    * mteamAdapter — M-Team v3 POST /api/torrent/search with x-api-key
    * rssAdapter — XML RSS/Atom feed parsing with keyword filter

Handler + routes:
  - handler/sites.go: listSites, getSite, createSite, updateSite,
    deleteSite, testSite, siteSearch handlers.
  - handler/handler.go: 7 new routes under authed group:
    GET/POST /sites, GET/PUT/DELETE /sites/:id,
    POST /sites/:id/test, GET /sites/search.

Frontend:
  - api/sites.ts: typed interfaces (Site, SiteSearchResult,
    CreateSiteInput) and sitesAPI helper.
  - pages/SitesPage.tsx: full CRUD UI with add-site form (site_type +
    auth_type selectors, cookie/api_key/rss inputs), connection test
    button with live status indicator, delete action.
  - pages/SiteSearchPage.tsx: cross-site search with keyword input,
    merged result table (site/title/size/seeders/leechers/free),
    one-click download-to-qBittorrent button.
  - App.tsx: lazy-loaded routes /sites and /site-search.
  - Layout.tsx: sidebar links 站点管理 + 站点搜索 under 自动化 group.

Verified: go build + go vet + go test pass; tsc -b + vite build passes
(32 lazy chunks, main bundle 255 KB / 85 KB gzipped).
2026-05-16 08:29:00 +00:00
Shuke 0be8c7952e Merge pull request #6 from ShukeBta/kiro/iter7-complete-port
feat: backup, notifier, organizer, douban, watch-history, poster-wall
2026-05-15 22:29:11 +08:00
Shuke 3a4689e950 Merge pull request #5 from ShukeBta/kiro/iter6-fix-and-harden
fix: context leak in goroutine handlers + harden smoke test
2026-05-15 22:27:36 +08:00
Kiro 778bf77437 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.
2026-05-15 14:17:26 +00:00
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
Shuke 2b03995e8a Merge pull request #4 from ShukeBta/kiro/iter5-e2e-test
test: end-to-end deployment smoke test (26 assertions, all passing)
2026-05-15 18:49:13 +08:00
Kiro da1a264d98 fix: context leak in goroutine handlers + harden smoke test
Bugs fixed:
  - handler/media.go: createLibraryHandler and deleteLibraryHandler
    spawned goroutines that called svc.Watcher.Refresh(c.Request.Context()).
    Since the HTTP request returns immediately, the context is cancelled
    before the watcher finishes. Use context.Background() so the refresh
    always completes.
  - handler/streaming.go: scrapeLibraryHandler had the same issue with
    c.Copy().Request.Context(). Fixed to context.Background().

Smoke test improvements:
  - Always create test media files (dummy or ffmpeg), always run scan and
    search — only ffprobe-specific assertions (width=320) and HLS are
    gated behind HAVE_FFMPEG.
  - ID is now always set (from library media list), so history/favourites
    /playlists tests never hit an unbound variable.
  - NFO + recycle-bin assertions no longer gated behind HAVE_FFMPEG since
    they work on any media row (even dummy files).

Verified: go build + go vet + go test all pass; 55/55 endpoint assertions
pass with 0 server 5xx; smoke test 26/26 PASS with and without ffmpeg.
2026-05-15 10:40:42 +00:00
Shuke a0ee132687 Update project references in README 2026-05-15 18:38:26 +08:00
Shuke 63da2f34c0 Merge pull request #3 from ShukeBta/kiro/iter4-providers-hwaccel-ai
feat: hwaccel + Fanart/TheTVDB + AI + Discover + NFO + Tasks + Recycle bin
2026-05-15 18:34:24 +08:00
Kiro 77e3fdd1f6 test: add E2E smoke test script + CI gate
After deploying the binary against a fresh data dir and exercising every
major REST endpoint by hand, codify the verification as a re-runnable
script.

scripts/smoke-test.sh:
  - Spins up bin/mediastation-go against a tempdir.
  - Generates 3 sample clips (movie, tv, anime) + an external SRT.
  - Walks 26 assertions: health → auth → libraries → scan → ffprobe →
    season parser → search → range stream → HLS playlist → external
    subtitle → history → favourites → playlists → RBAC (alice 403) →
    NFO export → recycle bin → SPA fallback → graceful shutdown.
  - Tears the server down via SIGTERM and reports pass/fail counts.
  - Exits non-zero on any failure so CI can use it as a deployment gate.

Local verification on this branch: PASS=26  FAIL=0.

Wiring:
  - Makefile: new 'smoke' target, depends on 'build'.
  - .github/workflows/ci.yml: third job 'smoke' that depends on backend
    + frontend, installs ffmpeg, builds both halves, then runs the
    script. The CI pipeline now blocks merges that break end-to-end
    deployability.
2026-05-14 16:36:34 +00:00
Shuke 15655a531a Merge pull request #2 from ShukeBta/kiro/iter3-comprehensive
feat: downloads, RSS, subtitles, Bangumi, watcher, stats, profile, audit
2026-05-15 00:25:34 +08:00
Shuke 9fe623cb9a Merge pull request #1 from ShukeBta/kiro/iter2-metadata-playback
feat(iter2): ffprobe metadata, TMDb scrape, HLS transcode, history / favourites / playlists
2026-05-15 00:24:29 +08:00
Kiro 8824638efb feat: hwaccel + Fanart/TheTVDB + AI + Discover + NFO + Tasks + Recycle bin
Backend
  - service/transcoder.go: encoder selector (software / nvenc / qsv /
    vaapi) with proper hwaccel + scale_* filters per encoder; configurable
    bitrate/preset/height/segment-seconds via transcoder.* config; new
    Active() snapshot + ActiveJob struct for the Tasks panel; unit-tested
    via buildFFmpegArgs(...).
  - service/thetvdb.go: TheTVDB v4 provider — login() caches the JWT for
    24h, SearchSeries() returns Match struct.
  - service/fanart.go: Fanart.tv provider — high-res movie artwork keyed
    by TMDb id; used to upgrade poster/backdrop after a successful match.
  - service/scraper.go: provider chain reworked — anime → Bangumi, tv →
    TheTVDB, default → TMDb, with optional Fanart upgrade post-match.
  - service/discover.go: TMDb /trending/movie/day + /movie/popular for
    the Discover rail.
  - service/ai.go: OpenAI-compatible client. SmartSearch() turns a free-
    form query into a structured SearchIntent JSON; Recommend() emits a
    short list of titles given the user's history. Disabled when
    ai.api_key is empty.
  - service/nfo.go: per-movie .nfo writer (Kodi/Jellyfin schema) +
    library-scope batch exporter.
  - service/media.go: SoftDelete / Restore / Purge / ListRecycleBin
    helpers backed by gorm Unscoped().
  - service/service.go: container wires Fanart, TheTVDB, Discover, AI,
    NFO; everything still tears down cleanly via Close().
  - config: new transcoder.* section (encoder, preset, video_bitrate,
    max_rate, buf_size, max_height, segment_seconds) with sensible
    defaults.

Handlers / routes
  - new files: discover.go, recycle.go, nfo.go, ai.go, tasks.go.
  - registers GET /tasks, /discover/{trending,popular},
    /ai/{status,search,recommend}; admin-only GET /recycle and
    DELETE/POST /media/:id (soft delete / restore / purge); admin-only
    POST /media/:id/nfo and /libraries/:id/nfo.

Frontend
  - new pages: DiscoverPage, TasksPage, RecycleBinPage; SearchPage gains
    an optional AI smart-search toggle that calls /api/ai/search.
  - MediaDetailPage gains admin actions for 导出 NFO and 移至回收站.
  - new api/ helpers: ai.ts, discover.ts, recycle.ts, tasks.ts.
  - App.tsx routes /discover, /tasks, /recycle (last two admin-only).
  - Layout sidebar groups now include Discover + Tasks + Recycle Bin.

Docker / config
  - Dockerfile: adds intel-media-driver / libva-utils / mesa-va-gallium
    so QSV + VAAPI work out of the box on Intel iGPUs.
  - docker-compose.yml: documents devices + group_add + nvidia runtime
    overrides for hardware transcoding.

Verified: go build, go vet, go test (incl. new TestBuildFFmpegArgs across
software / nvenc / qsv / vaapi encoder profiles); tsc -b && vite build
emits 22 route chunks plus the deferred hls chunk; main bundle 248 KB /
83 KB gzipped.
2026-05-14 16:22:08 +00:00
Kiro 7bd6bcfb1b feat: downloads, RSS, subtitles, Bangumi, watcher, stats, profile, audit
Backend
  - service/bangumi.go: Bangumi (bgm.tv) scraper for anime libraries.
  - service/episode_parser.go: SxxExx / NxE / EPxx / 第NN集 parser with
    unit tests; consumed by the scanner for tv/anime libraries.
  - service/scraper.go: provider chain orchestrator picks Bangumi for
    anime libraries (TMDb fallback), TMDb for everything else; emits
    'no_match' rows so we don't retry forever; AnyEnabled() for the
    scanner kick.
  - service/scanner.go: writes season/episode numbers for tv/anime libs
    and reports a 'probed' counter alongside 'added'.
  - service/transcoder.go (existing): unchanged, stays per-media.
  - service/subtitle.go: discovers external subtitles next to the source
    file (.srt/.vtt/.ass/.ssa) and converts SRT/ASS to WebVTT on the fly.
  - service/qbittorrent.go: thread-safe qBittorrent v2 Web UI client
    (login / add / list / delete) with cookie-jar reuse.
  - service/downloads.go: persists download tasks, reads runtime
    qbittorrent.* settings, polls /torrents/info every 5 s and pushes
    the result to WS subscribers ('download' topic).
  - service/subscription.go: 10-minute RSS poller with regex filter,
    GUID dedup persisted in the settings table, and 'subscription' WS
    events on enqueue.
  - service/watcher.go: fsnotify watcher with 5 s coalescing debouncer
    that triggers per-library rescans on create/rename/remove.
  - service/stats.go: dashboard snapshot — totals, recently added,
    gopsutil-driven CPU/mem/disk readings.
  - service/profile.go: non-credential profile patch + admin role mutator.
  - service/audit.go: best-effort writer for the access_logs table.
  - service/service.go: container wires every new service; Boot() spins
    up watcher / downloads poller / subscription scheduler; Close()
    tears them down on graceful shutdown.

Handlers
  - new files: downloads.go, subscriptions.go, subtitles.go, series.go,
    stats.go, profile.go, util.go.
  - handler.go: registers PATCH /me, /libraries/:id/seasons,
    /media/:id/subtitles, /subtitles/:id, /downloads*, /subscriptions*,
    /stats, /admin/users/:id/role.
  - auth.go / media.go: write audit rows for login + library CRUD and
    refresh the watcher when libraries change.

Frontend
  - api: new helpers for downloads, subscriptions, profile, series, stats,
    subtitles; library helper gained scrape().
  - hooks/useWebSocket.ts: shared connection with 3 s reconnect.
  - components/GlobalEvents.tsx: surfaces scan / scrape / subscription
    completion as toasts (mounted at app root).
  - pages: Library now switches to a season-grouped layout for tv/anime
    libraries; Player attaches WebVTT <track> elements; new pages for
    Downloads (live torrent table), Subscriptions, Profile, Stats.
  - components/Layout.tsx + App.tsx: sidebar groups (媒体库 / 自动化 /
    账号 / 管理) and routes for the new pages; /stats and /admin remain
    admin-only.
  - types/index.ts: new types — Subscription, DownloadTask, QBitTorrent,
    Hardware, StatsSnapshot.

Verified: go build, go vet, go test (incl. ParseEpisode + srtToVTT +
stripASSTags) all pass; tsc -b && vite build emits 17 route chunks plus
the deferred hls chunk; main bundle 247 KB / 83 KB gzipped.
2026-05-14 16:07:49 +00:00
Kiro 0f30c34463 feat: ffprobe + TMDb scrape + HLS transcode + history/favourites/playlists
Backend
  - service/ffprobe.go: thin ffprobe wrapper, parses duration / resolution /
    codecs into a typed ProbeResult. 30s per-file timeout.
  - service/tmdb.go: minimal TMDb provider (search/movie). Disabled when no
    api key; supports tmdb_api_proxy / tmdb_image_proxy overrides for users
    behind a firewall.
  - service/scraper.go: filename cleaner (handles bracketed tags, scene
    noise tokens, year extraction), per-row + per-library enrichment with a
    4 RPS throttle and WS hub progress events. Unit-tested.
  - service/scanner.go: now invokes ffprobe per file and kicks the TMDb
    scraper in the background once a library scan finishes.
  - service/transcoder.go: per-media ffmpeg HLS job manager; outputs
    index.m3u8 + seg_NNNNN.ts under cache/hls/<id>; cancels jobs on
    shutdown; publishes 'transcode' WS events.
  - service/stream.go: serves HLS playlist (with 30s wait-for-ready) and
    .ts segments with path-traversal protection. Adds Probe() helper used
    by the admin 'reprobe' button.
  - service/image_proxy.go: cached, host-allow-listed reverse proxy for
    TMDb / Bangumi / Douban / Fanart / TheTVDB images so the SPA never
    hits a CORS or GFW issue.
  - service/playback.go: history upsert, favourites toggle, playlist CRUD
    + ordered items. RecentHistory joins with model.Media in one extra
    query so the home page can render a 'Continue Watching' row.
  - handler/streaming.go + handler/playback.go: REST endpoints for HLS,
    image proxy, scrape (one + library), reprobe, history, favourites,
    playlists.
  - handler/handler.go: registers /api/hls/:id/{index.m3u8,:seg}, /api/img,
    /api/history, /api/favourites/:id, /api/playlists/* with proper
    auth/admin guards.

Frontend
  - api/client.ts: imageURL() helper; hlsURL() endpoint; reuses the JWT in
    a query parameter for <video src> and <img src>.
  - api/playback.ts: typed helpers for history, favourites, playlists.
  - components/MediaCard.tsx: optional 'progress' prop renders a thin
    bottom progress bar, used by the new Continue Watching row.
  - pages/HomePage.tsx: two rows (Continue Watching + Recently Added);
    falls back to the empty-state hint when both are empty.
  - pages/PlayerPage.tsx: hls.js (lazy-imported) with auto-fallback to
    direct play; ?mode=hls|direct query toggle; resume position written
    every 10s while playing.
  - pages/MediaDetailPage.tsx: heart toggle + admin 'rescrape' / 'reprobe'
    buttons + dedicated 'HLS 转码播放' CTA.
  - pages/FavouritesPage.tsx, PlaylistsPage.tsx, PlaylistDetailPage.tsx:
    new screens.
  - components/Layout.tsx + App.tsx: sidebar links for Favourites and
    Playlists; routes are now lazily code-split via React.lazy + Suspense
    so the initial bundle stays at ~243 KB / 82 KB gzipped (hls.js is
    fetched only on first HLS playback).

Verified: go build, go vet, go test (incl. CleanQuery cases) all pass;
frontend tsc -b && vite build emits 9 route chunks plus a deferred hls
chunk.
2026-05-14 15:44:31 +00:00
Kiro d5cf5fb4b2 feat: bootstrap MediaStationGo (Go + React rewrite of MediaStation)
Adopt the cropflre/nowen-video tech stack and rebuild the project from
scratch:

  - Backend: Go 1.25 + Gin + GORM + SQLite (WAL) + JWT + WebSocket hub.
    Layered packages config / database / model / repository / service /
    middleware / handler. Default admin (admin/admin123) seeded on first
    run; /api routes for auth, libraries, media, stream and admin
    panels. WebSocket scan-progress events at /api/ws.
  - Frontend: React 18 + Vite 5 + Tailwind 3.4 + Zustand + axios +
    react-router 6 + lucide-react + framer-motion + hls.js. Pages for
    Login / Home / Library / Search / MediaDetail / Player / Admin
    (Library, Users, Settings tabs).
  - Distribution: multi-arch Dockerfile (frontend -> backend -> Alpine
    runtime), docker-compose.yml, GitHub Actions for CI and GHCR
    publish, Makefile, env-prefixed config (MEDIASTATION_*).
  - Docs: README, CONTRIBUTING, .env.example, config.example.yaml.

Backend builds, vets and tests pass. Frontend builds via tsc -b && vite
build (250 kB JS / 16 kB CSS, gzipped 84 / 4 kB).
2026-05-14 15:26:29 +00:00