Soft-delete restore/purge/list and the recycle_purge job are gone.
Admin delete now removes library index rows, with an optional
delete_files query that also deletes local media + sidecar NFO.
UI copy is renamed from 移入回收站 to 删除, with a confirm checkbox.
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: truewhile <truewhile@users.noreply.github.com>
* Rebrand MMTL to MeBox across codebase and assets
Rename the project display name, Go module path, environment variable
prefix (MEBOX_*), Docker image references, and UI branding from MMTL/mmtl
to MeBox/mebox. Replace logo assets with the new MeBox icon and keep
legacy SQLite migration support for existing mmtl.db deployments.
Co-authored-by: truewhile <truewhile@users.noreply.github.com>
* Fix logo icons: use cube-only crop without truncated text
Previous icon generation cropped too much of the source image, including
partial MeBox wordmark text that was cut off in square icon containers.
Regenerate logo-64/192/512, favicon, and SVG from cube-only region.
Co-authored-by: truewhile <truewhile@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: truewhile <truewhile@users.noreply.github.com>
Root cause: display-only cloud library filtering was reused by scan jobs, merged cloud mounts could be skipped, and OpenList listing relied on WebDAV/first-page behavior that could cap huge directories around 100 items. Cloud scans also let new items consume probe budget before existing rows with missing track/artwork metadata.\n\nChanges:\n- split display filtering from scannable cloud filtering so merged cloud mounts still scan\n- add OpenList API pagination with WebDAV fallback\n- prioritize existing cloud media missing metadata before new imports\n- restrict automatic cloud sync to one successful 19:00-21:00 daily window and keep manual scan immediate\n- disable startup cloud full-scan by default, with an explicit opt-in setting\n- improve Emby/cloud playback compatibility and cache/search/test coverage from the continued work
- Organizer: add move/copy/hardlink/symlink transfer modes (default move),
per-request target_path/transfer_mode overrides, and honor organize.target_dir
/ organize.transfer_mode settings.
- keep_seeding (default on): escalate move->hardlink (cross-device->copy) so the
qBittorrent source stays in place and continues seeding after organize.
- qBittorrent SetLocation + POST /downloads/relocate to migrate whole torrents
while keeping them seeding.
- Scanner: FileID (device:inode) hardlink dedup to avoid duplicate recognition
and double-counted storage; extract single-file ingest.
- Watcher: recursive watch + incremental per-file ingest/remove instead of full
re-scan; periodic full library scan now gated behind scan.periodic_enabled
(default off) to reduce disk wear.
- Telegram bot: handle callback_query in polling, declare allowed_updates in
webhook, answer callbacks.
- Frontend: settings for transfer mode / keep_seeding / periodic scan; organize
panel target dir + transfer mode overrides.
- Tests for transfer modes, organizer resolution, SetLocation, inode dedup,
incremental ingest/remove.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
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.