track/track.go

Summary

Maintainability
A
0 mins
Test Coverage
// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package track

import (
    "net/url"
    "runtime"
    "strings"

    "github.com/bogem/nehm/util"
)

const clientID = "a3e059563d7fd3372b49b37f00a00bcf"

type Track struct {
    artist string
    title  string

    // Fields needed for JSON unmarshalling.
    JArtworkURL string `json:"artwork_url"`
    JCreatedAt  string `json:"created_at"`
    JDuration   int    `json:"duration"`
    JID         int    `json:"id"`
    JTitle      string `json:"title"`
    JURL        string `json:"stream_url"`
    JURI        string `json:"uri"`
    JAuthor     struct {
        AvatarURL string `json:"avatar_url"`
        Username  string `json:"username"`
    } `json:"user"`
}

func (t *Track) Artist() string {
    t.setArtistAndTitle()
    return t.artist
}

func (t Track) ArtworkURL() string {
    artworkURL := t.JArtworkURL
    if artworkURL == "" {
        artworkURL = t.JAuthor.AvatarURL
    }
    return strings.Replace(artworkURL, "large", "t500x500", 1)
}

func (t Track) Duration() string {
    return util.DurationString(util.ParseDuration(t.JDuration))
}

func (t Track) Filename() string {
    // Replace all filesystem non-friendly runes with the underscore.
    var toReplace string
    if runtime.GOOS == "windows" {
        toReplace = "<>:\"\\/|?*" // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
    } else {
        toReplace = ":/\\"
    }

    replaceRunes := func(r rune) rune {
        if strings.ContainsRune(toReplace, r) {
            return '_'
        }
        return r
    }

    return strings.Map(replaceRunes, t.Fullname()) + ".mp3"
}

func (t Track) Fullname() string {
    return t.Artist() + " — " + t.Title()
}

func (t Track) ID() int {
    return t.JID
}

// name splits track's title to artist and title if there is one of separators
// and sets to t.artist and to t.title respectively.
// If t.artist or t.title are not blank, it will do nothing.
// If there is no separator in title, it willt use t.JAuthor.Username and
// t.JTitle.
//
// E.g. if track has title "Michael Jackson - Thriller", then it will use
// "Michael Jackson" as artist and "Thriller" as title.
func (t *Track) setArtistAndTitle() {
    if t.artist != "" || t.title != "" {
        return
    }

    separators := [...]string{" - ", " ~ ", " – "}
    for _, sep := range separators {
        if strings.Contains(t.JTitle, sep) {
            splitted := strings.SplitN(t.JTitle, sep, 2)
            t.artist = strings.TrimSpace(splitted[0])
            t.title = strings.TrimSpace(splitted[1])
            return
        }
    }

    t.artist = strings.TrimSpace(t.JAuthor.Username)
    t.title = strings.TrimSpace(t.JTitle)
}

func (t *Track) Title() string {
    t.setArtistAndTitle()
    return t.title
}

func (t Track) URL() string {
    var streamURL string

    if t.JURL != "" {
        streamURL = t.JURL
    } else if t.JURI != "" {
        streamURL = t.JURI + "/stream"
    } else {
        return ""
    }

    u, err := url.Parse(streamURL)
    if err != nil || u.String() == "" {
        return ""
    }

    q := u.Query()
    q.Add("client_id", clientID)
    u.RawQuery = q.Encode()

    return u.String()
}

func (t Track) Year() string {
    return t.JCreatedAt[0:4]
}