feat: recursive WAV browsing in subdirectories
Handle ListWavs now uses filepath.WalkDir to find .wav files in all subdirectories. Returns relative paths (e.g. Kicks/kick.wav). Serves them correctly via /wavs/ prefix. Fixed: WalkDir swallows root errors differently than ReadDir — now detects and reports non-existent root directory errors. Added test for recursive subdirectory traversal.
This commit is contained in:
@@ -273,18 +273,30 @@ func handleListWavs(w http.ResponseWriter, r *http.Request) {
|
|||||||
dir = abs
|
dir = abs
|
||||||
}
|
}
|
||||||
|
|
||||||
entries, err := os.ReadDir(dir)
|
files := []string{}
|
||||||
|
var walkErr error
|
||||||
|
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
if path == dir {
|
||||||
|
walkErr = err
|
||||||
|
return filepath.SkipAll
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !d.IsDir() && strings.HasSuffix(strings.ToLower(d.Name()), ".wav") {
|
||||||
|
rel, _ := filepath.Rel(dir, path)
|
||||||
|
files = append(files, rel)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if walkErr != nil {
|
||||||
|
writeJSON(w, map[string]any{"error": walkErr.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeJSON(w, map[string]any{"error": err.Error()})
|
writeJSON(w, map[string]any{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
files := []string{}
|
|
||||||
for _, e := range entries {
|
|
||||||
if !e.IsDir() && strings.HasSuffix(strings.ToLower(e.Name()), ".wav") {
|
|
||||||
files = append(files, e.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeJSON(w, files)
|
writeJSON(w, files)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -473,6 +473,41 @@ func TestHandleListWavs_ReturnsWavFiles(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHandleListWavs_RecursiveSubdirs(t *testing.T) {
|
||||||
|
tmp := t.TempDir()
|
||||||
|
wavDir := filepath.Join(tmp, "wavs")
|
||||||
|
subDir := filepath.Join(wavDir, "Kicks")
|
||||||
|
if err := os.MkdirAll(subDir, 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fd, _ := os.Create(filepath.Join(wavDir, "root.wav"))
|
||||||
|
_ = fd.Close()
|
||||||
|
fd2, _ := os.Create(filepath.Join(subDir, "kick1.wav"))
|
||||||
|
_ = fd2.Close()
|
||||||
|
|
||||||
|
req := httptest.NewRequest("GET", "/api/list-wavs?dir="+wavDir, nil)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
handleListWavs(rr, req)
|
||||||
|
|
||||||
|
var files []string
|
||||||
|
if err := json.Unmarshal(rr.Body.Bytes(), &files); err != nil {
|
||||||
|
t.Fatalf("invalid JSON: %v", err)
|
||||||
|
}
|
||||||
|
if len(files) != 2 {
|
||||||
|
t.Errorf("expected 2 wav files, got %d: %v", len(files), files)
|
||||||
|
}
|
||||||
|
found := make(map[string]bool)
|
||||||
|
for _, f := range files {
|
||||||
|
found[f] = true
|
||||||
|
}
|
||||||
|
if !found["root.wav"] {
|
||||||
|
t.Errorf("missing root.wav, got %v", files)
|
||||||
|
}
|
||||||
|
if !found[filepath.Join("Kicks", "kick1.wav")] {
|
||||||
|
t.Errorf("missing Kicks/kick1.wav, got %v", files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestHandleListWavs_EmptyDir(t *testing.T) {
|
func TestHandleListWavs_EmptyDir(t *testing.T) {
|
||||||
tmp := t.TempDir()
|
tmp := t.TempDir()
|
||||||
req := httptest.NewRequest("GET", "/api/list-wavs?dir="+tmp, nil)
|
req := httptest.NewRequest("GET", "/api/list-wavs?dir="+tmp, nil)
|
||||||
@@ -486,6 +521,9 @@ func TestHandleListWavs_EmptyDir(t *testing.T) {
|
|||||||
if err := json.Unmarshal(rr.Body.Bytes(), &files); err != nil {
|
if err := json.Unmarshal(rr.Body.Bytes(), &files); err != nil {
|
||||||
t.Fatalf("invalid JSON: %v", err)
|
t.Fatalf("invalid JSON: %v", err)
|
||||||
}
|
}
|
||||||
|
if files == nil {
|
||||||
|
t.Fatal("expected empty array [], got null")
|
||||||
|
}
|
||||||
if len(files) != 0 {
|
if len(files) != 0 {
|
||||||
t.Errorf("expected 0 files, got %d", len(files))
|
t.Errorf("expected 0 files, got %d", len(files))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user