Files
MeBox/internal/service/fileid_unix.go
T
soldosluka857 b9b7b7052a feat(organize/scan): transfer modes, seeding-safe relocation, inode dedup, incremental scanning
- 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>
2026-05-30 12:01:46 +08:00

31 lines
800 B
Go

//go:build unix
package service
import (
"fmt"
"os"
"syscall"
)
// fileIdentity returns a stable "device:inode" identifier for the file at
// path. Hardlinks to the same data share an identity, which lets the scanner
// avoid importing the same physical file twice (e.g. a seeding source kept by
// keep_seeding and its organized hardlink). ok is false when the identity
// cannot be determined.
func fileIdentity(path string) (string, bool) {
fi, err := os.Stat(path)
if err != nil {
return "", false
}
st, ok := fi.Sys().(*syscall.Stat_t)
if !ok || st == nil {
return "", false
}
// 单链接文件没有去重意义,避免给独立文件也打上可碰撞的标识。
if st.Nlink < 2 {
return "", false
}
return fmt.Sprintf("%d:%d", uint64(st.Dev), uint64(st.Ino)), true
}