├── .drone.yml ├── .gitignore ├── .revive.toml ├── LICENSE ├── Makefile ├── README.md ├── albums.go ├── artist.go ├── category.go ├── charts.go ├── config.go ├── example └── main.go ├── feature.go ├── genre.go ├── go.mod ├── go.sum ├── hits.go ├── kkbox.go ├── kkbox_test.go ├── model.go ├── mood.go ├── release.go ├── search.go ├── shared.go └── tracks.go /.drone.yml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: pipeline 3 | name: default 4 | 5 | platform: 6 | os: linux 7 | arch: amd64 8 | 9 | clone: 10 | disable: true 11 | 12 | steps: 13 | - name: git 14 | pull: default 15 | image: plugins/git 16 | settings: 17 | depth: 50 18 | tags: true 19 | 20 | - name: test 21 | pull: always 22 | image: golang:1.11 23 | environment: 24 | GO111MODULE: on 25 | commands: 26 | - make vet 27 | - make lint 28 | - make embedmd 29 | - make test 30 | 31 | - name: codecov 32 | pull: default 33 | image: plugins/codecov 34 | settings: 35 | pattern: "**/coverage.txt" 36 | environment: 37 | CODECOV_TOKEN: 38 | from_secret: codecov_token 39 | 40 | - name: discord 41 | pull: always 42 | image: appleboy/drone-discord 43 | environment: 44 | DISCORD_WEBHOOK_ID: 45 | from_secret: discord_webhook_id 46 | DISCORD_WEBHOOK_TOKEN: 47 | from_secret: discord_webhook_token 48 | when: 49 | status: 50 | - success 51 | - failure 52 | 53 | ... 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | coverage.txt 16 | -------------------------------------------------------------------------------- /.revive.toml: -------------------------------------------------------------------------------- 1 | ignoreGeneratedHeader = false 2 | severity = "warning" 3 | confidence = 0.8 4 | errorCode = 1 5 | warningCode = 1 6 | 7 | [rule.blank-imports] 8 | [rule.context-as-argument] 9 | [rule.context-keys-type] 10 | [rule.dot-imports] 11 | [rule.error-return] 12 | [rule.error-strings] 13 | [rule.error-naming] 14 | [rule.exported] 15 | [rule.if-return] 16 | [rule.increment-decrement] 17 | [rule.var-naming] 18 | [rule.var-declaration] 19 | [rule.package-comments] 20 | [rule.range] 21 | [rule.receiver-naming] 22 | [rule.time-naming] 23 | [rule.unexported-return] 24 | [rule.indent-error-flow] 25 | [rule.errorf] 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Bo-Yi Wu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GO ?= go 2 | GOFMT ?= gofmt "-s" 3 | GOFILES := $(shell find . -name "*.go" -type f) 4 | PACKAGES ?= $(shell $(GO) list ./...) 5 | 6 | all: test 7 | 8 | .PHONY: fmt 9 | fmt: 10 | $(GOFMT) -w $(GOFILES) 11 | 12 | .PHONY: fmt-check 13 | fmt-check: 14 | @diff=$$($(GOFMT) -d $(GOFILES)); \ 15 | if [ -n "$$diff" ]; then \ 16 | echo "Please run 'make fmt' and commit the result:"; \ 17 | echo "$${diff}"; \ 18 | exit 1; \ 19 | fi; 20 | 21 | .PHONY: vet 22 | vet: 23 | $(GO) vet $(PACKAGES) 24 | 25 | lint: 26 | @hash revive > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ 27 | $(GO) get -u github.com/mgechev/revive; \ 28 | fi 29 | revive -config .revive.toml ./... || exit 1 30 | 31 | embedmd: 32 | @hash embedmd > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ 33 | $(GO) get -u github.com/campoy/embedmd; \ 34 | fi 35 | embedmd -d *.md 36 | 37 | test: fmt-check 38 | @$(GO) test -v -cover -coverprofile coverage.txt $(PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 39 | 40 | clean: 41 | $(GO) clean -x -i ./... 42 | rm -rf coverage.txt 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-kkbox 2 | 3 | [![GoDoc](https://godoc.org/github.com/appleboy/go-kkbox?status.svg)](https://godoc.org/github.com/appleboy/go-kkbox) 4 | [![Build Status](https://cloud.drone.io/api/badges/appleboy/go-kkbox/status.svg)](https://cloud.drone.io/appleboy/go-kkbox) 5 | [![codecov](https://codecov.io/gh/appleboy/go-kkbox/branch/master/graph/badge.svg)](https://codecov.io/gh/appleboy/go-kkbox) 6 | [![Go Report Card](https://goreportcard.com/badge/github.com/appleboy/go-kkbox)](https://goreportcard.com/report/github.com/appleboy/go-kkbox) 7 | 8 | [KKBOX Open API](https://docs-en.kkbox.codes/) SDK for Golang. 9 | 10 | ## Features 11 | 12 | * [x] Tracks 13 | - [x] /tracks/{track_id} 14 | * [x] Albums 15 | - [x] /albums/{album_id} 16 | - [x] /albums/{album_id}/tracks 17 | * [x] Artists 18 | - [x] /artists/{artist_id} 19 | - [x] /artists/{artist_id}/albums 20 | - [x] /artists/{artist_id}/top-tracks 21 | - [x] /artists/{artist_id}/related-artists 22 | * [x] Shared Playlists 23 | - [x] /shared-playlists/{playlist_id} 24 | - [x] /shared-playlists/{playlist_id}/tracks 25 | * [x] Featured Playlists 26 | - [x] /featured-playlists 27 | - [x] /featured-playlists/{playlist_id} 28 | - [x] /featured-playlists/{playlist_id}/tracks 29 | * [x] Featured Playlist Categories 30 | - [x] /featured-playlist-categories 31 | - [x] /featured-playlist-categories/{category_id} 32 | - [x] /featured-playlist-categories/{category_id}/playlists 33 | * [x] Mood Stations 34 | - [x] /mood-stations 35 | - [x] /mood-stations/{station_id} 36 | * [x] Genre Stations 37 | - [x] /genre-stations 38 | - [x] /genre-stations/{station_id} 39 | * [x] New Release Categories 40 | - [x] /new-release-categories 41 | - [x] /new-release-categories/{category_id} 42 | - [x] /new-release-categories/{category_id}/albums 43 | * [x] Search 44 | - [x] /search 45 | * [x] New Hits Playlists 46 | - [x] /new-hits-playlists 47 | - [x] /new-hits-playlists/{playlist_id} 48 | - [x] /new-hits-playlists/{playlist_id}/tracks 49 | * [x] Charts 50 | - [x] /charts 51 | - [x] /charts/{playlist_id} 52 | - [x] /charts/{playlist_id}/tracks 53 | 54 | ## Install 55 | 56 | Install SDK library 57 | 58 | ``` 59 | $ go get -u github.com/appleboy/go-kkbox 60 | ``` 61 | 62 | Inital the KKBox client: 63 | 64 | ```go 65 | package main 66 | 67 | import ( 68 | "log" 69 | "os" 70 | 71 | "github.com/appleboy/go-kkbox" 72 | ) 73 | 74 | var clientID = os.Getenv("CLIENT_ID") 75 | var clientSecret = os.Getenv("CLIENT_SECRET") 76 | 77 | func main() { 78 | if clientID == "" || clientSecret == "" { 79 | log.Fatal("missing client id or secret") 80 | } 81 | k, err := kkbox.New(clientID, clientSecret) 82 | 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | 87 | fmt.Println("====== kkbox client ======") 88 | spew.Dump(k) 89 | fmt.Println("====== kkbox end ======") 90 | 91 | // fetch charts 92 | charts, err := k.FetchCharts() 93 | if err != nil { 94 | fmt.Printf("%#v\n", err) 95 | } 96 | 97 | fmt.Printf("%#v\n", charts) 98 | 99 | ranks, err := k.FetchChartPlayList("4nUZM-TY2aVxZ2xaA-") 100 | if err != nil { 101 | fmt.Printf("%#v\n", err) 102 | } 103 | 104 | spew.Dump(ranks) 105 | 106 | tracks, err := k.FetchChartPlayListTrack("4nUZM-TY2aVxZ2xaA-", kkbox.Param{ 107 | PerPage: 1, 108 | Page: 2, 109 | Territory: "HK", 110 | }) 111 | if err != nil { 112 | fmt.Printf("%#v\n", err) 113 | } 114 | 115 | spew.Dump(tracks) 116 | log.Println("length: ", len(tracks.Data)) 117 | } 118 | ``` 119 | 120 | Run program: 121 | 122 | ```sh 123 | $ CLIENT_ID=xxx CLIENT_SECRET=xxx go run main.go 124 | ``` 125 | -------------------------------------------------------------------------------- /albums.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // FetchAlbum retrieve information of the album with {album_id}. 8 | func (b *Box) FetchAlbum(id string) (*Album, error) { 9 | resp := new(Album) 10 | url := fmt.Sprintf(AlbumURL, id) 11 | err := b.fetchData(url, resp) 12 | 13 | return resp, err 14 | } 15 | 16 | // FetchAlbumTrack list of tracks of an album.. 17 | func (b *Box) FetchAlbumTrack(id string, params ...Param) (*AlbumTrackData, error) { 18 | resp := new(AlbumTrackData) 19 | url := fmt.Sprintf(AlbumTrackURL, id) 20 | err := b.fetchData(url, resp, params...) 21 | 22 | return resp, err 23 | } 24 | -------------------------------------------------------------------------------- /artist.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // FetchArtist retrieve information of the artist with {artist_id}.. 8 | func (b *Box) FetchArtist(id string) (*Artist, error) { 9 | resp := new(Artist) 10 | url := fmt.Sprintf(ArtistURL, id) 11 | err := b.fetchData(url, resp) 12 | 13 | return resp, err 14 | } 15 | 16 | // FetchArtistAlbum list of albums of an artist. 17 | func (b *Box) FetchArtistAlbum(id string, params ...Param) (*ArtistAlbumData, error) { 18 | resp := new(ArtistAlbumData) 19 | url := fmt.Sprintf(ArtistAlbumURL, id) 20 | err := b.fetchData(url, resp, params...) 21 | 22 | return resp, err 23 | } 24 | 25 | // FetchArtistTopTrack List of top tracks of an artist. 26 | func (b *Box) FetchArtistTopTrack(id string, params ...Param) (*AlbumTrackData, error) { 27 | resp := new(AlbumTrackData) 28 | url := fmt.Sprintf(ArtistTopTrackURL, id) 29 | err := b.fetchData(url, resp, params...) 30 | 31 | return resp, err 32 | } 33 | 34 | // FetchArtistRelated List of top tracks of an artist. 35 | func (b *Box) FetchArtistRelated(id string, params ...Param) (*ArtistAlbumData, error) { 36 | resp := new(ArtistAlbumData) 37 | url := fmt.Sprintf(ArtistRelatedArtistURL, id) 38 | err := b.fetchData(url, resp, params...) 39 | 40 | return resp, err 41 | } 42 | -------------------------------------------------------------------------------- /category.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // FetchFeaturedCategory List of featured playlist categories. 8 | func (b *Box) FetchFeaturedCategory(params ...Param) (*GroupListData, error) { 9 | resp := new(GroupListData) 10 | url := FeaturedCategoryURL 11 | err := b.fetchData(url, resp, params...) 12 | 13 | return resp, err 14 | } 15 | 16 | // FetchSingleFeaturedCategory get a featured playlist category. 17 | func (b *Box) FetchSingleFeaturedCategory(id string, params ...Param) (*CategoryListData, error) { 18 | resp := new(CategoryListData) 19 | url := fmt.Sprintf(FeaturedSingleCategoryURL, id) 20 | err := b.fetchData(url, resp, params...) 21 | 22 | return resp, err 23 | } 24 | 25 | // FetchFeaturedCategoryPlayList list of playlists of a featured playlist category. 26 | func (b *Box) FetchFeaturedCategoryPlayList(id string, params ...Param) (*GroupListData, error) { 27 | resp := new(GroupListData) 28 | url := fmt.Sprintf(FeaturedCategoryPlayListURL, id) 29 | err := b.fetchData(url, resp, params...) 30 | 31 | return resp, err 32 | } 33 | -------------------------------------------------------------------------------- /charts.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // FetchCharts List of song rankings. 8 | func (b *Box) FetchCharts(params ...Param) (*GroupListData, error) { 9 | resp := new(GroupListData) 10 | url := ChartURL 11 | err := b.fetchData(url, resp, params...) 12 | 13 | return resp, err 14 | } 15 | 16 | // FetchChartPlayList to retrieve information of the song ranking with {playlist_id}. 17 | func (b *Box) FetchChartPlayList(platListID string, params ...Param) (*PlayListData, error) { 18 | resp := new(PlayListData) 19 | url := fmt.Sprintf(ChartPlayListURL, platListID) 20 | err := b.fetchData(url, resp, params...) 21 | 22 | return resp, err 23 | } 24 | 25 | // FetchChartPlayListTrack to retrieve information of the song ranking with {playlist_id}. 26 | func (b *Box) FetchChartPlayListTrack(platListID string, params ...Param) (*TrackData, error) { 27 | resp := new(TrackData) 28 | url := fmt.Sprintf(ChartPlayListTrackURL, platListID) 29 | err := b.fetchData(url, resp, params...) 30 | 31 | return resp, err 32 | } 33 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | const ( 4 | // OAuthTokenURL for auth token url 5 | OAuthTokenURL = "https://account.kkbox.com/oauth2/token" 6 | 7 | // ChartURL List of song rankings. 8 | ChartURL = "https://api.kkbox.com/v1.1/charts" 9 | // ChartPlayListURL retrieve information of the song ranking 10 | // with {playlist_id}. 11 | ChartPlayListURL = ChartURL + "/%s" 12 | // ChartPlayListTrackURL list tracks of a chart playlist. 13 | ChartPlayListTrackURL = ChartURL + "/%s/tracks" 14 | 15 | // NewHitURL List of new hits playlists. 16 | NewHitURL = "https://api.kkbox.com/v1.1/new-hits-playlists" 17 | // NewHitPlayListURL retrieve information of the new hits playlist 18 | // with {playlist_id}. 19 | NewHitPlayListURL = NewHitURL + "/%s" 20 | // NewHitPlayListTrackURL List of tracks of a new hits playlist. 21 | NewHitPlayListTrackURL = NewHitURL + "/%s/tracks" 22 | 23 | // SearchURL search for various objects. 24 | SearchURL = "https://api.kkbox.com/v1.1/search" 25 | 26 | // TrackURL retrieve information of the song with {track_id}. 27 | TrackURL = "https://api.kkbox.com/v1.1/tracks/%s" 28 | 29 | // AlbumURL retrieve information of the album with {album_id}. 30 | AlbumURL = "https://api.kkbox.com/v1.1/albums/%s" 31 | // AlbumTrackURL list of tracks of an album. 32 | AlbumTrackURL = AlbumURL + "/tracks" 33 | 34 | // ArtistURL retrieve information of the artist with {artist_id}. 35 | ArtistURL = "https://api.kkbox.com/v1.1/artists/%s" 36 | // ArtistAlbumURL List of albums of an artist. 37 | ArtistAlbumURL = ArtistURL + "/albums" 38 | // ArtistTopTrackURL List of top tracks of an artist. 39 | ArtistTopTrackURL = ArtistURL + "/top-tracks" 40 | // ArtistRelatedArtistURL Get related artists of an artist. 41 | ArtistRelatedArtistURL = ArtistURL + "/related-artists" 42 | 43 | // SharedPlaylistURL retrieve information of the shared playlist with {playlist_id}. 44 | SharedPlaylistURL = "https://api.kkbox.com/v1.1/shared-playlists/%s" 45 | // SharedPlaylistTrackURL list of songs of a shared playlist. 46 | SharedPlaylistTrackURL = "https://api.kkbox.com/v1.1/shared-playlists/%s/tracks" 47 | 48 | // FeaturedPlayListURL List of songs hand-picked and arranged by KKBOX editors. 49 | FeaturedPlayListURL = "https://api.kkbox.com/v1.1/featured-playlists" 50 | // FeaturedSinglePlayListURL retrieve information of the featured playlist with {playlist_id}. 51 | FeaturedSinglePlayListURL = FeaturedPlayListURL + "/%s" 52 | // FeaturedPlayListTrackURL List the songs of a featured playlist. 53 | FeaturedPlayListTrackURL = FeaturedPlayListURL + "/%s/tracks" 54 | 55 | // FeaturedCategoryURL List of featured playlist categories. 56 | FeaturedCategoryURL = "https://api.kkbox.com/v1.1/featured-playlist-categories" 57 | // FeaturedSingleCategoryURL Get A Featured Playlist Category. 58 | FeaturedSingleCategoryURL = FeaturedCategoryURL + "/%s" 59 | // FeaturedCategoryPlayListURL List of playlists of a featured playlist category. 60 | FeaturedCategoryPlayListURL = FeaturedCategoryURL + "/%s/playlists" 61 | 62 | // MoodStationURL List of mood stations. 63 | MoodStationURL = "https://api.kkbox.com/v1.1/mood-stations" 64 | // MoodSingleStationURL To retrieve information of the mood station with {station_id}. 65 | MoodSingleStationURL = MoodStationURL + "/%s" 66 | 67 | // GenreStationURL List of genre stations. 68 | GenreStationURL = "https://api.kkbox.com/v1.1/mood-stations" 69 | // GenreSingleStationURL retrieve information of the genre station with {station_id}. 70 | GenreSingleStationURL = GenreStationURL + "/%s" 71 | 72 | // ReleaseCategoryURL List of new release categories. 73 | ReleaseCategoryURL = "https://api.kkbox.com/v1.1/new-release-categories" 74 | // ReleaseSingleCategoryURL retrieve information of the new release category with {category_id}. 75 | ReleaseSingleCategoryURL = ReleaseCategoryURL + "/%s" 76 | // ReleaseCategoryAlbumURL List of albums of a new release category. 77 | ReleaseCategoryAlbumURL = ReleaseCategoryURL + "/%s/albums" 78 | ) 79 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/appleboy/go-kkbox" 9 | "github.com/davecgh/go-spew/spew" 10 | ) 11 | 12 | var clientID = os.Getenv("CLIENT_ID") 13 | var clientSecret = os.Getenv("CLIENT_SECRET") 14 | 15 | func main() { 16 | if clientID == "" || clientSecret == "" { 17 | log.Fatal("missing client id or secret") 18 | } 19 | k, err := kkbox.New(clientID, clientSecret) 20 | 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | // fetch charts 26 | charts, err := k.FetchCharts() 27 | if err != nil { 28 | fmt.Printf("%#v\n", err) 29 | } 30 | 31 | fmt.Printf("%#v\n", charts) 32 | 33 | ranks, err := k.FetchChartPlayList("4nUZM-TY2aVxZ2xaA-") 34 | if err != nil { 35 | fmt.Printf("%#v\n", err) 36 | } 37 | 38 | spew.Dump(ranks) 39 | 40 | tracks, err := k.FetchChartPlayListTrack("4nUZM-TY2aVxZ2xaA-", kkbox.Param{ 41 | PerPage: 1, 42 | Page: 2, 43 | Territory: "HK", 44 | }) 45 | if err != nil { 46 | fmt.Printf("%#v\n", err) 47 | } 48 | 49 | spew.Dump(tracks) 50 | log.Println("length: ", len(tracks.Data)) 51 | 52 | // // fetch hits 53 | // hits, err := k.FetchHits(kkbox.Param{ 54 | // PerPage: 2, 55 | // }) 56 | // if err != nil { 57 | // fmt.Printf("%#v\n", err) 58 | // } 59 | 60 | // fmt.Println("hit length:", len(hits.Data)) 61 | // // spew.Dump(hits) 62 | 63 | // list, err := k.FetchHitPlayList("DZrC8m29ciOFY2JAm3", kkbox.Param{ 64 | // Page: 2, 65 | // PerPage: 2, 66 | // }) 67 | // if err != nil { 68 | // fmt.Printf("%#v\n", err) 69 | // } 70 | 71 | // fmt.Println("list length:", len(list.Tracks.Data)) 72 | // // spew.Dump(list) 73 | 74 | // tracks, err := k.FetchHitPlayListTrack("DZrC8m29ciOFY2JAm3", kkbox.Param{ 75 | // PerPage: 3, 76 | // }) 77 | // if err != nil { 78 | // fmt.Printf("%#v\n", err) 79 | // } 80 | 81 | // fmt.Println("track length:", len(tracks.Data)) 82 | // // spew.Dump(tracks) 83 | 84 | // results, err := k.FetchSearchData("五月天", kkbox.Param{ 85 | // PerPage: 1, 86 | // }) 87 | // if err != nil { 88 | // fmt.Printf("%#v\n", err) 89 | // } 90 | 91 | // fmt.Println("artist length:", len(results.Artists.Data)) 92 | // fmt.Println("track length:", len(results.Tracks.Data)) 93 | // // spew.Dump(results) 94 | 95 | // track, err := k.FetchTrack("4kxvr3wPWkaL9_y3o_") 96 | // if err != nil { 97 | // fmt.Printf("%#v\n", err) 98 | // } 99 | // spew.Dump(track) 100 | 101 | // album, err := k.FetchAlbum("WpTPGzNLeutVFHcFq6") 102 | // if err != nil { 103 | // fmt.Printf("%#v\n", err) 104 | // } 105 | // spew.Dump(album) 106 | 107 | // albumTrack, err := k.FetchAlbumTrack("KmRKnW5qmUrTnGRuxF", kkbox.Param{ 108 | // Page: 1, 109 | // PerPage: 1, 110 | // }) 111 | // if err != nil { 112 | // fmt.Printf("%#v\n", err) 113 | // } 114 | // spew.Dump(albumTrack) 115 | // fmt.Println("album Track length:", len(albumTrack.Data)) 116 | 117 | // artist, err := k.FetchArtist("8q3_xzjl89Yakn_7GB") 118 | // if err != nil { 119 | // fmt.Printf("%#v\n", err) 120 | // } 121 | // spew.Dump(artist) 122 | 123 | // artistAlbums, err := k.FetchArtistAlbum("Cnv_K6i5Ft4y41SxLy", kkbox.Param{ 124 | // Page: 2, 125 | // PerPage: 1, 126 | // }) 127 | // if err != nil { 128 | // fmt.Printf("%#v\n", err) 129 | // } 130 | // spew.Dump(artistAlbums) 131 | 132 | // artistTopTracks, err := k.FetchArtistTopTrack("Cnv_K6i5Ft4y41SxLy", kkbox.Param{ 133 | // Page: 2, 134 | // PerPage: 1, 135 | // }) 136 | // if err != nil { 137 | // fmt.Printf("%#v\n", err) 138 | // } 139 | // spew.Dump(artistTopTracks) 140 | // fmt.Println("track length:", len(artistTopTracks.Data)) 141 | 142 | // artistRelated, err := k.FetchArtistRelated("8q3_xzjl89Yakn_7GB", kkbox.Param{ 143 | // Page: 1, 144 | // PerPage: 1, 145 | // }) 146 | // if err != nil { 147 | // fmt.Printf("%#v\n", err) 148 | // } 149 | // spew.Dump(artistRelated) 150 | // fmt.Println("artist releated length:", len(artistRelated.Data)) 151 | 152 | // sharedPlayList, err := k.FetchSharedPlayList("4nUZM-TY2aVxZ2xaA-") 153 | // if err != nil { 154 | // fmt.Printf("%#v\n", err) 155 | // } 156 | // spew.Dump(sharedPlayList) 157 | // fmt.Println("artist releated length:", len(sharedPlayList.Tracks.Data)) 158 | 159 | // sharedPlayListTrack, err := k.FetchSharedPlayListTrack("4nUZM-TY2aVxZ2xaA-", kkbox.Param{ 160 | // PerPage: 1, 161 | // Page: 5, 162 | // }) 163 | // if err != nil { 164 | // fmt.Printf("%#v\n", err) 165 | // } 166 | // spew.Dump(sharedPlayListTrack) 167 | // fmt.Println("artist releated length:", len(sharedPlayListTrack.Data)) 168 | 169 | // featured, err := k.FetchFeatured(kkbox.Param{ 170 | // PerPage: 2, 171 | // }) 172 | // if err != nil { 173 | // fmt.Printf("%#v\n", err) 174 | // } 175 | // spew.Dump(featured) 176 | // fmt.Println("feature play list length:", len(featured.Data)) 177 | 178 | // featuredPlayList, err := k.FetchFeaturedPlayList("Wt95My35CqR9hB_FW1") 179 | // if err != nil { 180 | // fmt.Printf("%#v\n", err) 181 | // } 182 | // spew.Dump(featuredPlayList) 183 | // fmt.Println("feature play list length:", len(featuredPlayList.Tracks.Data)) 184 | 185 | // featuredPlayListTrack, err := k.FetchFeaturedPlayListTrack("Wt95My35CqR9hB_FW1", kkbox.Param{ 186 | // PerPage: 2, 187 | // Page: 3, 188 | // }) 189 | // if err != nil { 190 | // fmt.Printf("%#v\n", err) 191 | // } 192 | // spew.Dump(featuredPlayListTrack) 193 | // fmt.Println("feature play list track length:", len(featuredPlayListTrack.Data)) 194 | 195 | // featuredCategory, err := k.FetchFeatured(kkbox.Param{ 196 | // PerPage: 2, 197 | // }) 198 | // if err != nil { 199 | // fmt.Printf("%#v\n", err) 200 | // } 201 | // spew.Dump(featuredCategory) 202 | // fmt.Println("feature category length:", len(featuredCategory.Data)) 203 | 204 | // featuredSingleCategory, err := k.FetchSingleFeaturedCategory("LXUR688EBKRRZydAWb") 205 | // if err != nil { 206 | // fmt.Printf("%#v\n", err) 207 | // } 208 | // spew.Dump(featuredSingleCategory) 209 | // fmt.Println("feature category play list length:", len(featuredSingleCategory.Playlists.Data)) 210 | 211 | // featuredCategoryPlayList, err := k.FetchFeaturedCategoryPlayList("LXUR688EBKRRZydAWb", kkbox.Param{ 212 | // PerPage: 2, 213 | // }) 214 | // if err != nil { 215 | // fmt.Printf("%#v\n", err) 216 | // } 217 | // spew.Dump(featuredCategoryPlayList) 218 | // fmt.Println("feature category play list length:", len(featuredCategoryPlayList.Data)) 219 | 220 | // moodStations, err := k.FetchMoodStationList(kkbox.Param{ 221 | // PerPage: 2, 222 | // }) 223 | // if err != nil { 224 | // fmt.Printf("%#v\n", err) 225 | // } 226 | // spew.Dump(moodStations) 227 | // fmt.Println("mood station list length:", len(moodStations.Data)) 228 | 229 | // moodStation, err := k.FetchMoodStation("StGZp2ToWq92diPHS7") 230 | // if err != nil { 231 | // fmt.Printf("%#v\n", err) 232 | // } 233 | // spew.Dump(moodStation) 234 | // fmt.Println("mood station list length:", len(moodStation.Tracks.Data)) 235 | 236 | // genreStations, err := k.FetchGenreStationList(kkbox.Param{ 237 | // PerPage: 2, 238 | // }) 239 | // if err != nil { 240 | // fmt.Printf("%#v\n", err) 241 | // } 242 | // spew.Dump(genreStations) 243 | // fmt.Println("mood station list length:", len(genreStations.Data)) 244 | 245 | // genreStation, err := k.FetchGenreStation("StGZp2ToWq92diPHS7") 246 | // if err != nil { 247 | // fmt.Printf("%#v\n", err) 248 | // } 249 | // spew.Dump(genreStation) 250 | // fmt.Println("mood station list length:", len(genreStation.Tracks.Data)) 251 | 252 | // releaseCategoryList, err := k.FetchReleaseCategory(kkbox.Param{ 253 | // PerPage: 2, 254 | // }) 255 | // if err != nil { 256 | // fmt.Printf("%#v\n", err) 257 | // } 258 | // spew.Dump(releaseCategoryList) 259 | // fmt.Println("release Category List length:", len(releaseCategoryList.Data)) 260 | 261 | // releaseCategory, err := k.FetchSingleReleaseCategory("KrdH2LdyUKS8z2aoxX", kkbox.Param{ 262 | // PerPage: 2, 263 | // }) 264 | // if err != nil { 265 | // fmt.Printf("%#v\n", err) 266 | // } 267 | // spew.Dump(releaseCategory) 268 | // fmt.Println("release Category List length:", len(releaseCategory.Albums.Data)) 269 | 270 | // releaseCategoryAlbum, err := k.FetchReleaseCategoryAlbum("KrdH2LdyUKS8z2aoxX", kkbox.Param{ 271 | // PerPage: 3, 272 | // }) 273 | // if err != nil { 274 | // fmt.Printf("%#v\n", err) 275 | // } 276 | // spew.Dump(releaseCategoryAlbum) 277 | // fmt.Println("release Category List length:", len(releaseCategoryAlbum.Data)) 278 | } 279 | -------------------------------------------------------------------------------- /feature.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // FetchFeatured List of songs hand-picked and arranged by KKBOX editors. 8 | func (b *Box) FetchFeatured(params ...Param) (*GroupListData, error) { 9 | resp := new(GroupListData) 10 | url := FeaturedPlayListURL 11 | err := b.fetchData(url, resp, params...) 12 | 13 | return resp, err 14 | } 15 | 16 | // FetchFeaturedPlayList retrieve information of the featured playlist with {playlist_id}. 17 | func (b *Box) FetchFeaturedPlayList(playList string, params ...Param) (*PlayListData, error) { 18 | resp := new(PlayListData) 19 | url := fmt.Sprintf(FeaturedSinglePlayListURL, playList) 20 | err := b.fetchData(url, resp, params...) 21 | 22 | return resp, err 23 | } 24 | 25 | // FetchFeaturedPlayListTrack list the songs of a featured playlist. 26 | func (b *Box) FetchFeaturedPlayListTrack(playList string, params ...Param) (*TrackData, error) { 27 | resp := new(TrackData) 28 | url := fmt.Sprintf(FeaturedPlayListTrackURL, playList) 29 | err := b.fetchData(url, resp, params...) 30 | 31 | return resp, err 32 | } 33 | -------------------------------------------------------------------------------- /genre.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // FetchGenreStationList List of genre stations. 8 | func (b *Box) FetchGenreStationList(params ...Param) (*GenreList, error) { 9 | resp := new(GenreList) 10 | url := GenreStationURL 11 | err := b.fetchData(url, resp, params...) 12 | 13 | return resp, err 14 | } 15 | 16 | // FetchGenreStation retrieve information of the genre station with {station_id}. 17 | func (b *Box) FetchGenreStation(id string, params ...Param) (*GenreData, error) { 18 | resp := new(GenreData) 19 | url := fmt.Sprintf(GenreSingleStationURL, id) 20 | err := b.fetchData(url, resp, params...) 21 | 22 | return resp, err 23 | } 24 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/appleboy/go-kkbox 2 | 3 | require ( 4 | github.com/astaxie/beego v0.0.0-20171218111859-f16688817aa4 5 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495 6 | ) 7 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/astaxie/beego v0.0.0-20171218111859-f16688817aa4 h1:S+9NpWgwkBlu/nZi3F4bkj5gcf5luYb4h5hc58f+tXo= 2 | github.com/astaxie/beego v0.0.0-20171218111859-f16688817aa4/go.mod h1:0R4++1tUqERR0WYFWdfkcrsyoVBCG4DgpDGokT3yb+U= 3 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495 h1:6IyqGr3fnd0tM3YxipK27TUskaOVUjU2nG45yzwcQKY= 4 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | -------------------------------------------------------------------------------- /hits.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // FetchHits List of new hits playlists. 8 | func (b *Box) FetchHits(params ...Param) (*GroupListData, error) { 9 | resp := new(GroupListData) 10 | url := ChartURL 11 | err := b.fetchData(url, resp, params...) 12 | 13 | return resp, err 14 | } 15 | 16 | // FetchHitPlayList retrieve information of the new hits playlist with {playlist_id}. 17 | func (b *Box) FetchHitPlayList(playList string, params ...Param) (*PlayListData, error) { 18 | resp := new(PlayListData) 19 | url := fmt.Sprintf(NewHitPlayListURL, playList) 20 | err := b.fetchData(url, resp, params...) 21 | 22 | return resp, err 23 | } 24 | 25 | // FetchHitPlayListTrack list of tracks of a new hits playlist. 26 | func (b *Box) FetchHitPlayListTrack(playList string, params ...Param) (*TrackData, error) { 27 | resp := new(TrackData) 28 | url := fmt.Sprintf(NewHitPlayListTrackURL, playList) 29 | err := b.fetchData(url, resp, params...) 30 | 31 | return resp, err 32 | } 33 | -------------------------------------------------------------------------------- /kkbox.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/base64" 6 | "errors" 7 | "strconv" 8 | 9 | "github.com/astaxie/beego/httplib" 10 | ) 11 | 12 | // Box struct 13 | type Box struct { 14 | ClientID string 15 | ClientSecret string 16 | Auth *AuthData 17 | Debug bool 18 | } 19 | 20 | // AuthData for access token 21 | type AuthData struct { 22 | AccessToken string `json:"access_token"` 23 | ExpiresIn int `json:"expires_in"` 24 | TokenType string `json:"token_type"` 25 | Error string `json:"error"` 26 | } 27 | 28 | var ( 29 | // ErrorMissingIDorSecret for missing client id or secret 30 | ErrorMissingIDorSecret = errors.New("missing client id or secret") 31 | // ErrorInvalidClient for wrong token 32 | ErrorInvalidClient = errors.New("invalid_client") 33 | ) 34 | 35 | // New MyAllocator object 36 | func New(id, secret string) (*Box, error) { 37 | if id == "" || secret == "" { 38 | return nil, ErrorMissingIDorSecret 39 | } 40 | 41 | box := &Box{ 42 | ClientID: id, 43 | ClientSecret: secret, 44 | } 45 | 46 | // get token 47 | auth, err := box.GetToken() 48 | box.Auth = auth 49 | 50 | if auth.Error != "" { 51 | return nil, ErrorInvalidClient 52 | } 53 | 54 | return box, err 55 | } 56 | 57 | func (b *Box) getCredential() string { 58 | data := []byte(b.ClientID + ":" + b.ClientSecret) 59 | return base64.StdEncoding.EncodeToString(data) 60 | } 61 | 62 | // GetToken get access token 63 | func (b *Box) GetToken() (*AuthData, error) { 64 | req := httplib.Post(OAuthTokenURL).Debug(b.Debug) 65 | req.Param("grant_type", "client_credentials") 66 | req.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) 67 | req.Header("Authorization", "Basic "+b.getCredential()) 68 | res := new(AuthData) 69 | err := req.ToJSON(res) 70 | return res, err 71 | } 72 | 73 | func (b *Box) fetchData(url string, res interface{}, params ...Param) error { 74 | req := httplib.Get(url).Debug(b.Debug) 75 | req.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) 76 | 77 | offset := 0 78 | limit := 10 79 | territory := "TW" 80 | q := "" 81 | contentType := "" 82 | if len(params) > 0 { 83 | param := params[0] 84 | if param.PerPage > 0 { 85 | limit = param.PerPage 86 | } 87 | if param.Page > 1 { 88 | offset = (param.Page - 1) * limit 89 | } 90 | if param.Territory != "" { 91 | territory = param.Territory 92 | } 93 | if param.Q != "" { 94 | q = param.Q 95 | } 96 | if param.Type != "" { 97 | contentType = param.Type 98 | } 99 | } 100 | 101 | // Add authorization header 102 | authorization := b.Auth.TokenType + " " + b.Auth.AccessToken 103 | req.Header("Authorization", authorization) 104 | 105 | req.Param("offset", strconv.Itoa(offset)) 106 | req.Param("limit", strconv.Itoa(limit)) 107 | req.Param("territory", territory) 108 | 109 | if q != "" { 110 | req.Param("q", q) 111 | } 112 | if contentType != "" { 113 | req.Param("type", contentType) 114 | } 115 | return req.ToJSON(res) 116 | } 117 | -------------------------------------------------------------------------------- /kkbox_test.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestBox_getBase64Encode(t *testing.T) { 9 | type fields struct { 10 | ClientID string 11 | ClientSecret string 12 | Debug bool 13 | } 14 | tests := []struct { 15 | name string 16 | fields fields 17 | want string 18 | }{ 19 | { 20 | name: "base 64 func", 21 | fields: fields{ 22 | ClientID: "client_id", 23 | ClientSecret: "client_secret", 24 | }, 25 | want: "Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=", 26 | }, 27 | } 28 | for _, tt := range tests { 29 | t.Run(tt.name, func(t *testing.T) { 30 | b := &Box{ 31 | ClientID: tt.fields.ClientID, 32 | ClientSecret: tt.fields.ClientSecret, 33 | Debug: tt.fields.Debug, 34 | } 35 | if got := b.getCredential(); got != tt.want { 36 | t.Errorf("Box.getBase64Encode() = %v, want %v", got, tt.want) 37 | } 38 | }) 39 | } 40 | } 41 | 42 | func TestBox_GetToken(t *testing.T) { 43 | type fields struct { 44 | ClientID string 45 | ClientSecret string 46 | Auth *AuthData 47 | Debug bool 48 | } 49 | tests := []struct { 50 | name string 51 | fields fields 52 | want *AuthData 53 | wantErr bool 54 | }{ 55 | { 56 | name: "base 64 func", 57 | fields: fields{ 58 | ClientID: "client_id", 59 | ClientSecret: "client_secret", 60 | }, 61 | want: &AuthData{ 62 | AccessToken: "", 63 | ExpiresIn: 0, 64 | TokenType: "", 65 | Error: "invalid_client", 66 | }, 67 | wantErr: false, 68 | }, 69 | } 70 | for _, tt := range tests { 71 | t.Run(tt.name, func(t *testing.T) { 72 | b := &Box{ 73 | ClientID: tt.fields.ClientID, 74 | ClientSecret: tt.fields.ClientSecret, 75 | Auth: tt.fields.Auth, 76 | Debug: tt.fields.Debug, 77 | } 78 | got, err := b.GetToken() 79 | if (err != nil) != tt.wantErr { 80 | t.Errorf("Box.GetToken() error = %v, wantErr %v", err, tt.wantErr) 81 | return 82 | } 83 | if !reflect.DeepEqual(got, tt.want) { 84 | t.Errorf("Box.GetToken() = %v, want %v", got, tt.want) 85 | } 86 | }) 87 | } 88 | } 89 | 90 | func TestNew(t *testing.T) { 91 | type args struct { 92 | id string 93 | secret string 94 | } 95 | tests := []struct { 96 | name string 97 | args args 98 | want *Box 99 | wantErr bool 100 | errorMsg error 101 | }{ 102 | { 103 | name: "missing id", 104 | args: args{ 105 | id: "", 106 | secret: "1234", 107 | }, 108 | want: nil, 109 | wantErr: true, 110 | errorMsg: ErrorMissingIDorSecret, 111 | }, 112 | { 113 | name: "missing secret", 114 | args: args{ 115 | id: "1234", 116 | secret: "", 117 | }, 118 | want: nil, 119 | wantErr: true, 120 | errorMsg: ErrorMissingIDorSecret, 121 | }, 122 | { 123 | name: "wrong id and secret", 124 | args: args{ 125 | id: "1234", 126 | secret: "1234", 127 | }, 128 | want: nil, 129 | wantErr: true, 130 | errorMsg: ErrorInvalidClient, 131 | }, 132 | } 133 | for _, tt := range tests { 134 | t.Run(tt.name, func(t *testing.T) { 135 | got, err := New(tt.args.id, tt.args.secret) 136 | if (err != nil) != tt.wantErr { 137 | t.Errorf("New() error = %v, wantErr %v", err, tt.wantErr) 138 | return 139 | } 140 | 141 | if err != nil && err != tt.errorMsg { 142 | t.Errorf("New() error = %v, wantErr %v", err, tt.errorMsg) 143 | return 144 | } 145 | 146 | if got != nil { 147 | if got.ClientID != tt.want.ClientID { 148 | t.Errorf("ClientID = %v, want %v", got.ClientID, tt.want.ClientID) 149 | return 150 | } 151 | 152 | if got.ClientSecret != tt.want.ClientSecret { 153 | t.Errorf("ClientSecret = %v, want %v", got.ClientSecret, tt.want.ClientSecret) 154 | return 155 | } 156 | } 157 | }) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /model.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // Summary page data 8 | type Summary struct { 9 | Total int `json:"total"` 10 | } 11 | 12 | // Owner data 13 | type Owner struct { 14 | ID string `json:"id"` 15 | Name string `json:"name"` 16 | } 17 | 18 | // Image for music 19 | type Image struct { 20 | Height int `json:"height"` 21 | Width int `json:"width"` 22 | URL string `json:"url"` 23 | } 24 | 25 | // Paging data 26 | type Paging struct { 27 | Offset int `json:"offset"` 28 | Limit int `json:"limit"` 29 | Previous string `json:"previous"` 30 | Next string `json:"next"` 31 | } 32 | 33 | // Artist struct 34 | type Artist struct { 35 | ID string `json:"id"` 36 | Name string `json:"name"` 37 | URL string `json:"url"` 38 | Images []Image `json:"images"` 39 | } 40 | 41 | // Album data 42 | type Album struct { 43 | ID string `json:"id"` 44 | Name string `json:"name"` 45 | URL string `json:"url"` 46 | Explicitness bool `json:"explicitness"` 47 | AvailableTerritories []string `json:"available_territories"` 48 | ReleaseDate string `json:"release_date"` 49 | Images []Image `json:"images"` 50 | Artist Artist `json:"artist"` 51 | } 52 | 53 | // Track data 54 | type Track struct { 55 | ID string `json:"id"` 56 | Name string `json:"name"` 57 | Duration int `json:"duration"` 58 | URL string `json:"url"` 59 | TrackNumber int `json:"track_number"` 60 | Explicitness bool `json:"explicitness"` 61 | AvailableTerritories []string `json:"available_territories"` 62 | Album Album `json:"album"` 63 | } 64 | 65 | // Song for play list 66 | type Song struct { 67 | ID string `json:"id"` 68 | Title string `json:"title"` 69 | Description string `json:"description"` 70 | URL string `json:"url"` 71 | Images []Image `json:"images"` 72 | UpdatedAt time.Time `json:"updated_at"` 73 | Owner Owner `json:"owner"` 74 | } 75 | 76 | // GroupListData for song list 77 | type GroupListData struct { 78 | Data []Song `json:"data"` 79 | Paging Paging `json:"paging"` 80 | Summary Summary `json:"summary"` 81 | } 82 | 83 | // PlayListData to retrieve information of playlist 84 | type PlayListData struct { 85 | Tracks struct { 86 | Data []Track `json:"data"` 87 | Paging Paging `json:"paging"` 88 | Summary Summary `json:"summary"` 89 | } `json:"tracks"` 90 | ID string `json:"id"` 91 | Title string `json:"title"` 92 | Description string `json:"description"` 93 | URL string `json:"url"` 94 | Images []Image `json:"images"` 95 | UpdatedAt time.Time `json:"updated_at"` 96 | Owner Owner `json:"owner"` 97 | } 98 | 99 | // TrackData List tracks of a chart playlist. 100 | type TrackData struct { 101 | Data []Track `json:"data"` 102 | Paging Paging `json:"paging"` 103 | Summary Summary `json:"summary"` 104 | } 105 | 106 | // Param for http get parameter 107 | type Param struct { 108 | // The search keywords, URL encoded. 109 | Q string 110 | // The content type. Content type could be track, album, artist, or playlist. 111 | Type string 112 | // Territory code, i.e. TW, HK, SG, MY, JP, of search content. 113 | Territory string 114 | Page int 115 | // The number of items to return per page, not to exceed 50. 116 | PerPage int 117 | } 118 | 119 | // SearchData for search results 120 | type SearchData struct { 121 | Artists struct { 122 | Data []Artist `json:"data"` 123 | Paging Paging `json:"paging"` 124 | Summary Summary `json:"summary"` 125 | } `json:"artists"` 126 | Tracks struct { 127 | Data []Track `json:"data"` 128 | Paging Paging `json:"paging"` 129 | Summary Summary `json:"summary"` 130 | } `json:"tracks"` 131 | Paging Paging `json:"paging"` 132 | Summary Summary `json:"summary"` 133 | } 134 | 135 | // AlbumTrackData list of tracks of an album. 136 | type AlbumTrackData struct { 137 | Data []Track `json:"data"` 138 | Paging Paging `json:"paging"` 139 | Summary Summary `json:"summary"` 140 | } 141 | 142 | // ArtistAlbumData list of albums of an artist. 143 | type ArtistAlbumData struct { 144 | Data []Album `json:"data"` 145 | Paging Paging `json:"paging"` 146 | Summary Summary `json:"summary"` 147 | } 148 | 149 | // CategoryListData for category 150 | type CategoryListData struct { 151 | ID string `json:"id"` 152 | Title string `json:"title"` 153 | Images []Image `json:"images"` 154 | Playlists GroupListData `json:"playlists"` 155 | } 156 | 157 | // MoodListData mood stations. 158 | type MoodListData struct { 159 | Data []struct { 160 | ID string `json:"id"` 161 | Name string `json:"name"` 162 | Images []Image `json:"images"` 163 | } `json:"data"` 164 | Paging Paging `json:"paging"` 165 | Summary Summary `json:"summary"` 166 | } 167 | 168 | // MoodData retrieve information of the mood station with {station_id}. 169 | type MoodData struct { 170 | ID string `json:"id"` 171 | Name string `json:"name"` 172 | Tracks struct { 173 | Data []Track `json:"data"` 174 | Paging Paging `json:"paging"` 175 | Summary Summary `json:"summary"` 176 | } `json:"tracks"` 177 | } 178 | 179 | // GenreList List of genre stations. 180 | type GenreList struct { 181 | Data []struct { 182 | ID string `json:"id"` 183 | Category string `json:"category"` 184 | Name string `json:"name"` 185 | } `json:"data"` 186 | Paging Paging `json:"paging"` 187 | Summary Summary `json:"summary"` 188 | } 189 | 190 | // GenreData retrieve information of the genre station with {station_id}. 191 | type GenreData struct { 192 | Category string `json:"category"` 193 | ID string `json:"id"` 194 | Name string `json:"name"` 195 | Tracks struct { 196 | Data []Track `json:"data"` 197 | Paging Paging `json:"paging"` 198 | Summary Summary `json:"summary"` 199 | } `json:"tracks"` 200 | } 201 | 202 | // ReleaseCategoryList List of new release categories. 203 | type ReleaseCategoryList struct { 204 | Data []struct { 205 | ID string `json:"id"` 206 | Title string `json:"title"` 207 | } `json:"data"` 208 | Paging Paging `json:"paging"` 209 | Summary Summary `json:"summary"` 210 | } 211 | 212 | // AlbumList retrieve information of the new release category with {category_id}. 213 | type AlbumList struct { 214 | ID string `json:"id"` 215 | Title string `json:"title"` 216 | Albums struct { 217 | Data []Album `json:"data"` 218 | Paging Paging `json:"paging"` 219 | Summary Summary `json:"summary"` 220 | } `json:"albums"` 221 | } 222 | -------------------------------------------------------------------------------- /mood.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // FetchMoodStationList List of mood stations. 8 | func (b *Box) FetchMoodStationList(params ...Param) (*MoodListData, error) { 9 | resp := new(MoodListData) 10 | url := MoodStationURL 11 | err := b.fetchData(url, resp, params...) 12 | 13 | return resp, err 14 | } 15 | 16 | // FetchMoodStation retrieve information of the mood station with {station_id}. 17 | func (b *Box) FetchMoodStation(id string, params ...Param) (*MoodData, error) { 18 | resp := new(MoodData) 19 | url := fmt.Sprintf(MoodSingleStationURL, id) 20 | err := b.fetchData(url, resp, params...) 21 | 22 | return resp, err 23 | } 24 | -------------------------------------------------------------------------------- /release.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // FetchReleaseCategory List of new release categories. 8 | func (b *Box) FetchReleaseCategory(params ...Param) (*ReleaseCategoryList, error) { 9 | resp := new(ReleaseCategoryList) 10 | url := ReleaseCategoryURL 11 | err := b.fetchData(url, resp, params...) 12 | 13 | return resp, err 14 | } 15 | 16 | // FetchSingleReleaseCategory retrieve information of the new release category with {category_id}. 17 | func (b *Box) FetchSingleReleaseCategory(id string, params ...Param) (*AlbumList, error) { 18 | resp := new(AlbumList) 19 | url := fmt.Sprintf(ReleaseSingleCategoryURL, id) 20 | err := b.fetchData(url, resp, params...) 21 | 22 | return resp, err 23 | } 24 | 25 | // FetchReleaseCategoryAlbum list of albums of a new release category. 26 | func (b *Box) FetchReleaseCategoryAlbum(id string, params ...Param) (*ArtistAlbumData, error) { 27 | resp := new(ArtistAlbumData) 28 | url := fmt.Sprintf(ReleaseCategoryAlbumURL, id) 29 | err := b.fetchData(url, resp, params...) 30 | 31 | return resp, err 32 | } 33 | -------------------------------------------------------------------------------- /search.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | // FetchSearchData Search for various objects.. 4 | func (b *Box) FetchSearchData(key string, params ...Param) (*SearchData, error) { 5 | resp := new(SearchData) 6 | 7 | if len(params) == 0 { 8 | params = append(params, Param{ 9 | Q: key, 10 | }) 11 | } else { 12 | params[0].Q = key 13 | } 14 | 15 | err := b.fetchData(SearchURL, resp, params...) 16 | 17 | return resp, err 18 | } 19 | -------------------------------------------------------------------------------- /shared.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // FetchSharedPlayList retrieve information of the shared playlist with {playlist_id}. 8 | func (b *Box) FetchSharedPlayList(id string, params ...Param) (*PlayListData, error) { 9 | resp := new(PlayListData) 10 | url := fmt.Sprintf(SharedPlaylistURL, id) 11 | err := b.fetchData(url, resp, params...) 12 | 13 | return resp, err 14 | } 15 | 16 | // FetchSharedPlayListTrack list of songs of a shared playlist.. 17 | func (b *Box) FetchSharedPlayListTrack(id string, params ...Param) (*TrackData, error) { 18 | resp := new(TrackData) 19 | url := fmt.Sprintf(SharedPlaylistTrackURL, id) 20 | err := b.fetchData(url, resp, params...) 21 | 22 | return resp, err 23 | } 24 | -------------------------------------------------------------------------------- /tracks.go: -------------------------------------------------------------------------------- 1 | package kkbox 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // FetchTrack retrieve information of the song with {track_id}. 8 | func (b *Box) FetchTrack(id string) (*Track, error) { 9 | resp := new(Track) 10 | url := fmt.Sprintf(TrackURL, id) 11 | err := b.fetchData(url, resp) 12 | 13 | return resp, err 14 | } 15 | --------------------------------------------------------------------------------