1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
package fetch
import (
"os"
"path/filepath"
"sort"
"testing"
)
func TestPrettifyName_RestoresSpaces(t *testing.T) {
got := prettifyName("01 - The_Law_of_Gravitation.mp3")
if got != "01 - The Law of Gravitation.mp3" {
t.Errorf("got %q", got)
}
}
// yt-dlp's --restrict-filenames collapses odd characters into underscores, and
// replacing them can leave runs of spaces behind.
func TestPrettifyName_CollapsesRepeatedSpaces(t *testing.T) {
got := prettifyName("02 - A___Gap.mp3")
if got != "02 - A Gap.mp3" {
t.Errorf("got %q", got)
}
}
// Sources whose titles already begin with a track number produce
// "01 - 01 - April.mp3" once the output template prepends its own index. Seen
// for real against a LibriVox collection on the first live run.
func TestPrettifyName_CollapsesDuplicatedTrackPrefix(t *testing.T) {
cases := []struct{ in, want string }{
{"01 - 01 - April.mp3", "01 - April.mp3"},
{"02 - 02 - Ave Atque Vale.mp3", "02 - Ave Atque Vale.mp3"},
{"03 - 03_-_The_Average_Man.mp3", "03 - The Average Man.mp3"},
{"10 - 10 - Tenth.mp3", "10 - Tenth.mp3"},
}
for _, c := range cases {
if got := prettifyName(c.in); got != c.want {
t.Errorf("prettifyName(%q) = %q, want %q", c.in, got, c.want)
}
}
}
// Only collapse when the two numbers agree. A title that genuinely starts with
// a different number is not a duplicated prefix and must be left alone.
func TestPrettifyName_KeepsDistinctLeadingNumbers(t *testing.T) {
cases := []struct{ in, want string }{
{"01 - 1964 - The Messenger Lectures.mp3", "01 - 1964 - The Messenger Lectures.mp3"},
{"05 - 12 Angry Men.mp3", "05 - 12 Angry Men.mp3"},
}
for _, c := range cases {
if got := prettifyName(c.in); got != c.want {
t.Errorf("prettifyName(%q) = %q, want %q", c.in, got, c.want)
}
}
}
func TestPrettifyName_LeavesCleanNamesAlone(t *testing.T) {
for _, n := range []string{"01 - April.mp3", "Introduction.mp3"} {
if got := prettifyName(n); got != n {
t.Errorf("prettifyName(%q) = %q, want it unchanged", n, got)
}
}
}
func TestRename_RenamesOnDisk(t *testing.T) {
dir := t.TempDir()
for _, n := range []string{"01 - 01 - April.mp3", "02 - The_Average_Man.mp3", ".podkit-archive"} {
if err := os.WriteFile(filepath.Join(dir, n), []byte("x"), 0o644); err != nil {
t.Fatal(err)
}
}
if _, err := Rename(dir); err != nil {
t.Fatalf("Rename: %v", err)
}
entries, _ := os.ReadDir(dir)
var names []string
for _, e := range entries {
names = append(names, e.Name())
}
sort.Strings(names)
want := []string{".podkit-archive", "01 - April.mp3", "02 - The Average Man.mp3"}
if len(names) != len(want) {
t.Fatalf("got %v, want %v", names, want)
}
for i := range want {
if names[i] != want[i] {
t.Errorf("got %v, want %v", names, want)
break
}
}
}
// Renaming must never destroy an existing file to make a name prettier.
func TestRename_DoesNotClobber(t *testing.T) {
dir := t.TempDir()
if err := os.WriteFile(filepath.Join(dir, "01 - April.mp3"), []byte("original"), 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "01 - 01 - April.mp3"), []byte("candidate"), 0o644); err != nil {
t.Fatal(err)
}
if _, err := Rename(dir); err != nil {
t.Fatalf("Rename: %v", err)
}
body, err := os.ReadFile(filepath.Join(dir, "01 - April.mp3"))
if err != nil {
t.Fatal(err)
}
if string(body) != "original" {
t.Error("an existing file was overwritten by the rename")
}
}
func TestRename_MissingDirectoryIsAnError(t *testing.T) {
if _, err := Rename(filepath.Join(t.TempDir(), "nope")); err == nil {
t.Error("expected an error for a missing directory")
}
}
// Check must explain what to install, not just report a lookup failure.
func TestErrNotInstalled_SaysHowToFixIt(t *testing.T) {
e := &ErrNotInstalled{}
for _, want := range []string{"yt-dlp", "pip install", "ffmpeg"} {
if !contains(e.Error(), want) {
t.Errorf("error message should mention %q; got %q", want, e.Error())
}
}
}
func contains(h, n string) bool {
return len(h) >= len(n) && (func() bool {
for i := 0; i+len(n) <= len(h); i++ {
if h[i:i+len(n)] == n {
return true
}
}
return false
})()
}
|