Add configurable shuffle depth for copy order
ShuffleDepth in DiskProfile controls how files are selected:
-1 = no shuffle (preserve source order)
0 = all files in random order
N = group files by folder at depth N from /media, shuffle groups,
copy entire group before moving to next
Exposed in disk profile UI as a select with level descriptions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,8 @@ type Options struct {
|
||||
OverwriteMode config.OverwriteMode
|
||||
FileSelectMode config.FileSelectMode
|
||||
Transcode *disk.TranscodeProfile // nil = не транскодировать
|
||||
// ShuffleDepth: -1=выкл, 0=файлы вразнобой, 1+=группировка по папке на глубине N
|
||||
ShuffleDepth int
|
||||
}
|
||||
|
||||
type Copier struct {
|
||||
@@ -254,8 +256,7 @@ func (c *Copier) run(ctx context.Context, taskID string, opts Options, database
|
||||
return
|
||||
}
|
||||
|
||||
// случайный порядок — выбираем что копировать до начала копирования
|
||||
rand.Shuffle(len(files), func(i, j int) { files[i], files[j] = files[j], files[i] })
|
||||
files = applyShuffleDepth(files, opts.ShuffleDepth)
|
||||
|
||||
_, free, err := disk.DiskUsage(opts.MountPath)
|
||||
if err != nil {
|
||||
@@ -723,3 +724,52 @@ func copyFile(ctx context.Context, src, dst string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyShuffleDepth упорядочивает файлы по заданной глубине шафлера.
|
||||
// depth < 0 → оригинальный порядок (без шафла)
|
||||
// depth == 0 → все файлы перемешиваются случайно
|
||||
// depth >= 1 → файлы группируются по папке на уровне depth от корня /media,
|
||||
// группы перемешиваются, внутри каждой группы порядок сохраняется.
|
||||
func applyShuffleDepth(files []fileEntry, depth int) []fileEntry {
|
||||
if depth < 0 || len(files) == 0 {
|
||||
return files
|
||||
}
|
||||
if depth == 0 {
|
||||
rand.Shuffle(len(files), func(i, j int) { files[i], files[j] = files[j], files[i] })
|
||||
return files
|
||||
}
|
||||
|
||||
type group struct {
|
||||
key string
|
||||
files []fileEntry
|
||||
}
|
||||
groupMap := make(map[string]*group, 64)
|
||||
var order []string
|
||||
for _, f := range files {
|
||||
key := folderKeyAtDepth(f.relPath, depth)
|
||||
if _, ok := groupMap[key]; !ok {
|
||||
groupMap[key] = &group{key: key}
|
||||
order = append(order, key)
|
||||
}
|
||||
groupMap[key].files = append(groupMap[key].files, f)
|
||||
}
|
||||
rand.Shuffle(len(order), func(i, j int) { order[i], order[j] = order[j], order[i] })
|
||||
|
||||
result := make([]fileEntry, 0, len(files))
|
||||
for _, key := range order {
|
||||
result = append(result, groupMap[key].files...)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// folderKeyAtDepth возвращает путь к папке глубины depth из relPath.
|
||||
// relPath вида "anime/Naruto/Season1/ep01.mkv", depth=2 → "anime/Naruto"
|
||||
func folderKeyAtDepth(relPath string, depth int) string {
|
||||
relPath = filepath.ToSlash(relPath)
|
||||
parts := strings.Split(relPath, "/")
|
||||
maxDepth := len(parts) - 1 // последний элемент — имя файла
|
||||
if depth >= maxDepth {
|
||||
depth = maxDepth
|
||||
}
|
||||
return strings.Join(parts[:depth], "/")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user