docs: document packages, exported API and non-obvious logic

This commit is contained in:
Mathis Maquenne
2026-08-06 13:04:10 +02:00
parent 5dfd832d0d
commit b1ad9b4904
41 changed files with 757 additions and 7 deletions
+16
View File
@@ -14,12 +14,16 @@ type Contributors struct {
Authors []string `json:"author"`
}
// UnmarshalJSON tolerates the empty array Deezer sends when a track has no
// contributors. The field is an object in every other case, so decoding it
// straight into the struct fails on those tracks.
func (c *Contributors) UnmarshalJSON(data []byte) error {
if string(data) == "[]" {
*c = Contributors{}
return nil
}
// Alias drops the method set, so this Unmarshal does not recurse.
type Alias Contributors
aux := (*Alias)(c)
@@ -47,6 +51,10 @@ func (t *Track) FullTitle() string {
return t.Title
}
// Filename builds the on-disk name for the track, sanitised for the current
// filesystem. Album downloads get a zero padded track number prefix so the
// directory sorts in playing order; the other kinds have no meaningful
// ordering to preserve.
func (t *Track) Filename(kind Kind, format string) string {
ext := "mp3"
switch format {
@@ -67,11 +75,19 @@ func (t *Track) Filename(kind Kind, format string) string {
base := fmt.Sprintf("%s%s - %s", prefix, t.Artist, t.FullTitle())
base, _ = filenamify.Filenamify(base, filenamify.Options{MaxLength: 255})
// 255 bytes is the per-component limit on ext4 and APFS. The budget also
// has to cover the extension, its dot, and the "-id3v2" suffix the tagging
// library appends to its temporary file: without that headroom, tagging a
// long title fails after the download has already succeeded.
base = truncateBytes(base, 255-len(ext)-1-len("-id3v2"))
return base + "." + ext
}
// truncateBytes shortens s to at most maxLen bytes without splitting a rune.
// The limit is in bytes because that is what filesystems enforce, but cutting
// mid-rune would leave an invalid UTF-8 name, so it backs up to the last rune
// boundary that fits.
func truncateBytes(s string, maxLen int) string {
if maxLen <= 0 {
return ""