docs: document packages, exported API and non-obvious logic
This commit is contained in:
@@ -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 ""
|
||||
|
||||
Reference in New Issue
Block a user