2026-05-22 20:26:11 -04:00
|
|
|
package playback
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
2026-07-03 02:23:14 +08:00
|
|
|
"time"
|
2026-05-22 20:26:11 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// CleanupOrphanedTranscodeDirs removes per-session transcode directories that
|
|
|
|
|
// are not associated with any currently active session IDs.
|
2026-07-03 02:23:14 +08:00
|
|
|
//
|
|
|
|
|
// minAge spares a dir whose most recent modification is younger than the given
|
|
|
|
|
// age even when it is absent from activeSessionIDs. Under token-carried
|
|
|
|
|
// reconstruction there is no durable card index, so an in-memory miss does not
|
|
|
|
|
// prove a session is dead — a client holding a still-valid token may yet
|
|
|
|
|
// reconstruct it. Sparing dirs younger than the maximum token lifetime closes
|
|
|
|
|
// that race; once a dir is older than any surviving token, it is safe to reap.
|
|
|
|
|
// Pass 0 to disable age-sparing (e.g. a dedicated node's boot-time full wipe,
|
|
|
|
|
// where node restart is an accepted session loss).
|
|
|
|
|
func CleanupOrphanedTranscodeDirs(root string, activeSessionIDs map[string]struct{}, minAge time.Duration) (int, error) {
|
2026-05-22 20:26:11 -04:00
|
|
|
if root == "" {
|
|
|
|
|
return 0, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entries, err := os.ReadDir(root)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
return 0, nil
|
|
|
|
|
}
|
|
|
|
|
return 0, fmt.Errorf("read transcode dir %q: %w", root, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
removed := 0
|
2026-07-03 02:23:14 +08:00
|
|
|
cutoff := time.Now().Add(-minAge)
|
2026-05-22 20:26:11 -04:00
|
|
|
for _, entry := range entries {
|
|
|
|
|
if !entry.IsDir() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, ok := activeSessionIDs[entry.Name()]; ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dir := filepath.Join(root, entry.Name())
|
2026-07-03 02:23:14 +08:00
|
|
|
if minAge > 0 {
|
|
|
|
|
info, statErr := entry.Info()
|
|
|
|
|
if statErr != nil {
|
|
|
|
|
// Can't verify age: conservatively retain rather than risk
|
|
|
|
|
// reaping a dir a surviving token could still reconstruct.
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if info.ModTime().After(cutoff) {
|
|
|
|
|
// Recently active: a surviving token could still reconstruct it.
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-22 20:26:11 -04:00
|
|
|
if err := os.RemoveAll(dir); err != nil {
|
|
|
|
|
return removed, fmt.Errorf("remove orphaned transcode dir %q: %w", dir, err)
|
|
|
|
|
}
|
|
|
|
|
removed++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return removed, nil
|
|
|
|
|
}
|