Skip to content

Commit

Permalink
Fixup naming / documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
joshkunz committed Oct 11, 2021
1 parent 3aedbdf commit 2dac16d
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 49 deletions.
8 changes: 4 additions & 4 deletions fakelib.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ func main() {
log.Fatalf("failed to load golden file %q: %v", goldenPath, err)
}
lib.Tracks = *librarySize
lib.Tagger = library.AlbumTagger{
TracksPerAlbum: *tracksPerAlbum,
AlbumsPerArtist: *albumsPerArtist,
MinPathLength: *minPathLength,
lib.Tagger = library.RepeatedLetters{
TracksPerAlbum: *tracksPerAlbum,
AlbumsPerArtist: *albumsPerArtist,
MinComponentLength: *minPathLength / 3,
}.Tag

// No need for the file anymore, just close it to drop the handle.
Expand Down
87 changes: 42 additions & 45 deletions library/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,41 +80,36 @@ func (s Song) Read(buf []byte, off int64) {
copy(buf, s.data[off:])
}

// Songs in the library are always generated in the form:
// <artist>/<album>/<track>.mp3
// Where each component is some number of characters from A-Z.
// RepeatedLetters implements a tagger to generate track metadata using
// repeated letters. Each component is some number of characters from A-Z.
// Artists/Albums/Tracks are named in-order, starting at 0. So track 0 is
// A/A/A.mp3
// Artist: A, Album: A, Title: A
// Track 1 is:
// A/A/B.mp3
// Artist: A, Album: A, Title: B
// etc.
// Track metadata represents what is shown in the path, except that the track
// title is the concatenation of <artist>, <album>, <track> with "-" as a
// separator.
//
// When MinPathLength > 3, path components are duplicated to extend the length
// of the path, while maintaining uniqueness. E.g., when MinPathLength = 4,
// Track 0 is:
// AA/AA/AA.mp3
// When MinComponentLength is set, track components are duplicated to extend
// the length of the path, while maintaining uniqueness. E.g., when
// MinComponentLength = 2, Track 0 is:
// Artist: AA, Album: AA, Title: AA
//
// When all letters have been exhausted in a category, the name is extended
// following a "spreadsheet" schema: A, B, ..., Z, AA, AB, ..., ZZ, AAA, ...
// When MinPathLength > 3, the repeated name is extended. So when
// MinPathLength = 4, "AB" becomes "ABAB".
type AlbumTagger struct {
// When MinComponentLength is set, the repeated name is extended. So when
// MinComponentLength = 2, "AB" becomes "ABAB".
type RepeatedLetters struct {
TracksPerAlbum int
AlbumsPerArtist int
// Number of artists is derived from #of tracks, the track/album, and
// album/artist ratios.
// Number of artists is derived from the he track/album, and album/artist
// ratios.

// The minimum length of a path. Path elements are repeated to extend this
// value. Must be >= 3 or the result is undefined.
MinPathLength int
// The minimum length of a component. Components are repeated to extend
// this value. Defaults to 1 if unset.
MinComponentLength int
}

var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

func letterName(i int) string {
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var name []byte
for {
name = append(name, letters[i%len(letters)])
Expand All @@ -132,21 +127,17 @@ func letterName(i int) string {
return string(name)
}

func (a AlbumTagger) name(i int) string {
minLength := a.MinPathLength
func (a RepeatedLetters) name(i int) string {
minLength := a.MinComponentLength
if minLength == 0 {
// Special case to make the zero-value useful. Assume 3.
minLength = 3
}
// Divide by 3 because our paths have 3 components.
extension := minLength / 3
if minLength%3 != 0 {
extension++
// Special case to make the zero-value useful. Assume 1.
minLength = 1
}
return strings.Repeat(letterName(i), extension)
return strings.Repeat(letterName(i), minLength)
}

func (a AlbumTagger) Tag(idx int) *id3v2.Tag {
// Tag implements TagFunc to generate an id3v2 tag for a song at each index.
func (a RepeatedLetters) Tag(idx int) *id3v2.Tag {
artist := a.name(idx / (a.TracksPerAlbum * a.AlbumsPerArtist))
album := a.name((idx / a.TracksPerAlbum) % a.AlbumsPerArtist)
trackIdx := idx % a.TracksPerAlbum
Expand All @@ -167,6 +158,16 @@ func (a AlbumTagger) Tag(idx int) *id3v2.Tag {
return t
}

// ArtistAlbumTitle implements PathFunc. The generated path follows a typical
// <artist>/<album>/<title>.mp3 pattern for the song's title.
func ArtistAlbumTitle(index int, tag *id3v2.Tag) string {
artist := tag.Artist()
album := tag.Album()
title := tag.Title()

return path.Join(artist, album, title) + ".mp3"
}

// TagFunc is a function that generates the tag for the song at the given
// index in the library.
type TagFunc func(index int) *id3v2.Tag
Expand All @@ -175,14 +176,6 @@ type TagFunc func(index int) *id3v2.Tag
// the given index and tag.
type PathFunc func(index int, tag *id3v2.Tag) string

func ArtistAlbumTitlePather(index int, tag *id3v2.Tag) string {
artist := tag.Artist()
album := tag.Album()
title := tag.Title()

return path.Join(artist, album, title) + ".mp3"
}

// Library represents a fake library of songs. A single "golden" MP3 is
// used as the basis for every track in the library, and song metadata is
// generated on a per-track basis. A new library can be created with `New`.
Expand All @@ -192,7 +185,11 @@ type Library struct {
// Total number of tracks in the fake library.
Tracks int

// Tagger is invoked to retrieve the tags for the song at each index
// position (0-based).
Tagger TagFunc
// Pather is invoked to generate the path for the song at each index. It
// is also passed the tag generated by the Tagger.
Pather PathFunc

// golden is the "golden" track data for this
Expand Down Expand Up @@ -225,7 +222,8 @@ func (l *Library) SongAt(idx int) (Song, error) {
return Song{tag: buf.Bytes(), data: l.golden}, nil
}

// New returns a new Library that uses Golden data
// New returns a new Library that uses Golden data read from the given golden
// reader.
func New(golden io.ReadSeeker) (*Library, error) {
header, err := id3v2.ParseReader(golden, id3v2.Options{Parse: true})
if err != nil {
Expand All @@ -244,12 +242,11 @@ func New(golden io.ReadSeeker) (*Library, error) {

return &Library{
Tracks: 1000,
Tagger: AlbumTagger{
Tagger: RepeatedLetters{
TracksPerAlbum: 10,
AlbumsPerArtist: 3,
MinPathLength: 3,
}.Tag,
Pather: ArtistAlbumTitlePather,
Pather: ArtistAlbumTitle,
golden: data,
}, nil
}

0 comments on commit 2dac16d

Please sign in to comment.