initial commit
This commit is contained in:
commit
e439b6c309
61
auth.go
Normal file
61
auth.go
Normal file
@ -0,0 +1,61 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"github.com/zmb3/spotify"
|
||||
)
|
||||
|
||||
var oauthToken string
|
||||
|
||||
func StartAuthorizationCodeFlow() (authClient *spotify.Client) {
|
||||
// first start an HTTP server
|
||||
http.HandleFunc("/callback", completeAuth)
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// log.Println("Got request for:", r.URL.String())
|
||||
})
|
||||
go http.ListenAndServe(":8080", nil)
|
||||
|
||||
url := auth.AuthURL(state)
|
||||
fmt.Println("Please log in to Spotify by visiting the following page in your browser:\n", url)
|
||||
|
||||
client := <-ch
|
||||
|
||||
oauthToken = extractToken(client)
|
||||
|
||||
// use the client to make calls that require authorization
|
||||
user, spotify_auth_err := client.CurrentUser()
|
||||
if spotify_auth_err != nil {
|
||||
log.Fatal(spotify_auth_err)
|
||||
}
|
||||
fmt.Println("You are logged in as:", user.ID)
|
||||
return client
|
||||
}
|
||||
|
||||
func completeAuth(w http.ResponseWriter, r *http.Request) {
|
||||
tok, err := auth.Token(state, r)
|
||||
if err != nil {
|
||||
http.Error(w, "Couldn't get token", http.StatusForbidden)
|
||||
log.Fatal(err)
|
||||
}
|
||||
if st := r.FormValue("state"); st != state {
|
||||
http.NotFound(w, r)
|
||||
log.Fatalf("State mismatch: %s != %s\n", st, state)
|
||||
}
|
||||
// use the token to get an authenticated client
|
||||
client := auth.NewClient(tok)
|
||||
status = 2
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
fmt.Fprint(w, "test <script>window.close()</script>")
|
||||
ch <- &client
|
||||
}
|
||||
|
||||
func extractToken(client *spotify.Client) (tokenString string) {
|
||||
token, err := client.Token()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return token.AccessToken
|
||||
}
|
68
freedom.go
Normal file
68
freedom.go
Normal file
@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func DownloadTrack() {
|
||||
fmt.Print("Please provide a spotify track id: ")
|
||||
// track_id
|
||||
var track_id string
|
||||
_, _ = fmt.Scanln(&track_id)
|
||||
|
||||
// folder
|
||||
var folder = root_track_folder
|
||||
var userFolder string
|
||||
|
||||
if check_if_track_is_valid(track_id) {
|
||||
fmt.Print("Please provide a folder name: "+root_track_folder)
|
||||
_, _ = fmt.Scanln(&userFolder)
|
||||
} else {
|
||||
fmt.Println("Not a valid spotify track id.\nPlease provide a correct one.\n")
|
||||
waitForInput()
|
||||
main()
|
||||
}
|
||||
|
||||
lens := len(userFolder)
|
||||
var newVal string
|
||||
if lens > 0 {
|
||||
newVal = userFolder[lens-1:]
|
||||
} else {
|
||||
newVal = folder[len(folder)-1:]
|
||||
}
|
||||
if string(newVal) != "/" {
|
||||
userFolder = userFolder + "/"
|
||||
}
|
||||
|
||||
folder = folder + userFolder
|
||||
fmt.Println(folder)
|
||||
|
||||
download_track_to_folder(track_id,folder)
|
||||
|
||||
waitForInput()
|
||||
}
|
||||
|
||||
func DownloadPlaylist() {
|
||||
fmt.Print("Please provide a spotify playlist id: ")
|
||||
// playlist_id
|
||||
var playlist_id string
|
||||
_, _ = fmt.Scanln(&playlist_id)
|
||||
|
||||
fmt.Print("Please provide the user id of the playlists owner: ")
|
||||
// owner_id
|
||||
var owner_id string
|
||||
_, _ = fmt.Scanln(&owner_id)
|
||||
|
||||
download_playlist_to_folder(playlist_id,owner_id,root_playlists_folder)
|
||||
waitForInput()
|
||||
}
|
||||
|
||||
func DownloadAllPlaylistsOfUser() {
|
||||
// fmt.Print("Please provide a spotify user id: ")
|
||||
// user_id
|
||||
var user_id = ""
|
||||
// _, _ = fmt.Scanln(&user_id)
|
||||
|
||||
download_all_playlist_of_user(user_id,root_user_folder+user_id+"/")
|
||||
waitForInput()
|
||||
}
|
20
help.go
Normal file
20
help.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import(
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func HowToSetEnvVariables() {
|
||||
fmt.Println("=> How To: Set Client ID")
|
||||
fmt.Println("Set the SPOTIFY_ID environment variable")
|
||||
fmt.Println("Examples:") // YOUTUBE_TOKEN
|
||||
fmt.Println(" $ export SPOTIFY_ID=\"aebff49ecd9e446ba573989feda8cb05\" && \\")
|
||||
fmt.Println(" export SPOTIFY_SECRET=\"a43f00a0d8734109aef7c881062eb97b\" && \\")
|
||||
fmt.Println(" export YOUTUBE_TOKEN=\"AIzaSyBbaWwezUJgCdYAxYyqViGcVsRRpWgTKLw\"")
|
||||
fmt.Println("")
|
||||
fmt.Println(" $ setenv SPOTIFY_ID \"aebff49ecd9e446ba573989feda8cb05\" && setenv SPOTIFY_SECRET=\"a43f00a0d8734109aef7c881062eb97b\" && \\")
|
||||
fmt.Println(" setenv SPOTIFY_SECRET \"a43f00a0d8734109aef7c881062eb97b\" && \\")
|
||||
fmt.Println(" setenv YOUTUBE_TOKEN \"AIzaSyBbaWwezUJgCdYAxYyqViGcVsRRpWgTKLw\"")
|
||||
|
||||
waitForInput()
|
||||
}
|
89
main.go
Normal file
89
main.go
Normal file
@ -0,0 +1,89 @@
|
||||
// This example demonstrates how to authenticate with Spotify using the authorization code flow.
|
||||
// In order to run this example yourself, you'll need to:
|
||||
//
|
||||
// 1. Register an application at: https://developer.spotify.com/my-applications/
|
||||
// - Use "http://localhost:8080/callback" as the redirect URI
|
||||
// 2. Set the SPOTIFY_ID environment variable to the client ID you got in step 1.
|
||||
// 3. Set the SPOTIFY_SECRET environment variable to the client secret from step 1.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/dixonwille/wmenu"
|
||||
|
||||
"gopkg.in/dixonwille/wlog.v2"
|
||||
"github.com/zmb3/spotify"
|
||||
"github.com/ahmetalpbalkan/go-cursor"
|
||||
)
|
||||
|
||||
// and enter this value.
|
||||
const redirectURI = "http://localhost:8080/callback"
|
||||
const root_track_folder = "music/tracks/"
|
||||
const root_playlists_folder = "music/playlists/"
|
||||
const root_user_folder = "music/users/"
|
||||
|
||||
var (
|
||||
auth = get_spotify_auth();
|
||||
ch = make(chan *spotify.Client)
|
||||
state = "abc123"
|
||||
status = 1
|
||||
client *spotify.Client
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
menu := wmenu.NewMenu("Choose your Option (0-9). <return> to get back to menu.")
|
||||
menu.Action(
|
||||
func (opts []wmenu.Opt) error {
|
||||
switch opts[0].Value {
|
||||
case 1:
|
||||
HowToSetEnvVariables()
|
||||
case 2:
|
||||
fmt.Println("~ starting 'Authorization Code Flow' Sequence ~")
|
||||
client = StartAuthorizationCodeFlow()
|
||||
case 3:
|
||||
fmt.Println("~ starting 'Dowload a Track' Sequence ~")
|
||||
DownloadTrack()
|
||||
case 4:
|
||||
fmt.Println("~ starting 'Download a Playlist' Sequence ~")
|
||||
DownloadPlaylist()
|
||||
case 5:
|
||||
fmt.Println("~ starting 'Download all of my Playlists' Sequence ~")
|
||||
DownloadAllPlaylistsOfUser()
|
||||
case 6:
|
||||
fmt.Println("~ starting 'Go to webapp' Sequence~")
|
||||
startWebServer()
|
||||
}
|
||||
main()
|
||||
return nil
|
||||
})
|
||||
|
||||
fmt.Print(cursor.ClearEntireScreen())
|
||||
fmt.Print(cursor.MoveTo(0,0))
|
||||
if status > 1 {
|
||||
fmt.Println("~ status => authenticated ~")
|
||||
menu.Option(" Download a Track", 3, true, nil)
|
||||
menu.Option(" Download a Playlist", 4, true, nil)
|
||||
menu.Option(" Download all of my Playlists", 5, true, nil)
|
||||
menu.Option(" Go to webapp", 6, true, nil)
|
||||
} else {
|
||||
fmt.Println("~ status => not authenticated ~")
|
||||
menu.Option(" Set Client ID", 1, true, nil)
|
||||
menu.Option(" Start Authorization Code Flow", 2, false, nil)
|
||||
}
|
||||
|
||||
optionColor := wlog.None
|
||||
questionColor := wlog.Color{}
|
||||
responseColor := wlog.Color{}
|
||||
errorColor := wlog.Color{}
|
||||
menu.AddColor(optionColor, questionColor, responseColor, errorColor)
|
||||
// menu.AddColor("green", "blue", "yellow", "yellow")
|
||||
|
||||
mainMenu := menu.Run()
|
||||
if mainMenu != nil{
|
||||
log.Fatal(mainMenu)
|
||||
}
|
||||
|
||||
}
|
53
manual-download.log
Normal file
53
manual-download.log
Normal file
@ -0,0 +1,53 @@
|
||||
Ótta - Sólstafir
|
||||
Ótta - Sólstafir
|
||||
Ótta - Sólstafir
|
||||
Ótta - Sólstafir
|
||||
Ótta - Sólstafir
|
||||
Ótta - Sólstafir
|
||||
Ótta - Sólstafir
|
||||
Sie nannten ihn Putte - Erdmöbel
|
||||
L'Exèrcit Que Vindrà - Els Pets
|
||||
Þar Sem Enginn Fer - Sjálfviljugur - Árstíðir
|
||||
Morgunn - Svavar Knútur
|
||||
L'échappée - Barbagallo
|
||||
La millor flor del balcó - La iaia
|
||||
Posa'm més gin, David! - Mishima
|
||||
Univers parallèles - Damien Robitaille
|
||||
Croisières méditerranéennes - Bernard Lavilliers
|
||||
Oppure no - Greta Panettieri
|
||||
L'emmerdeuse - Eskelina
|
||||
Bättre än så här - Pernilla Andersson
|
||||
Dråba i sjøen - Tønes
|
||||
Sommartjej - Josef Park
|
||||
Arrastarte-Ei - Céu
|
||||
Nonedaless - Wermonster
|
||||
Borsch Antistar - Wermonster
|
||||
Von den Identitäten - Manfred Groove
|
||||
Gorilla Scapes - Lützenkirchen
|
||||
Pillenkäfer - Boris Brejcha
|
||||
Liitäjä - Antti Rasi
|
||||
Ótta - Sólstafir
|
||||
Ótta - Sólstafir
|
||||
Ótta - Sólstafir
|
||||
Sie nannten ihn Putte - Erdmöbel
|
||||
L'Exèrcit Que Vindrà - Els Pets
|
||||
Þar Sem Enginn Fer - Sjálfviljugur - Árstíðir
|
||||
Ótta - Sólstafir
|
||||
Sie nannten ihn Putte - Erdmöbel
|
||||
Ótta - Sólstafir
|
||||
Sie nannten ihn Putte - Erdmöbel
|
||||
L'Exèrcit Que Vindrà - Els Pets
|
||||
Þar Sem Enginn Fer - Sjálfviljugur - Árstíðir
|
||||
Morgunn - Svavar Knútur
|
||||
Ótta - Sólstafir
|
||||
Sie nannten ihn Putte - Erdmöbel
|
||||
L'Exèrcit Que Vindrà - Els Pets
|
||||
Þar Sem Enginn Fer - Sjálfviljugur - Árstíðir
|
||||
Morgunn - Svavar Knútur
|
||||
Ótta - Sólstafir
|
||||
Ótta - Sólstafir
|
||||
Ótta - Sólstafir
|
||||
Sie nannten ihn Putte - Erdmöbel
|
||||
L'Exèrcit Que Vindrà - Els Pets
|
||||
Þar Sem Enginn Fer - Sjálfviljugur - Árstíðir
|
||||
Morgunn - Svavar Knútur
|
6
pages/head.html
Normal file
6
pages/head.html
Normal file
@ -0,0 +1,6 @@
|
||||
{{define "head"}}
|
||||
<head>
|
||||
<title>freedom - for your music</title>
|
||||
<link href="http://localhost:8088/static/tables.css" type="text/css" rel="stylesheet" />
|
||||
</head>
|
||||
{{end}}
|
27
pages/playlists.html
Normal file
27
pages/playlists.html
Normal file
@ -0,0 +1,27 @@
|
||||
{{define "playlists"}}
|
||||
<html>
|
||||
{{template "head"}}
|
||||
<body>
|
||||
|
||||
<form method="post" name="submitDownloadQueue" action="http://localhost:8088/submitDownloadQueue">
|
||||
<input type="submit">
|
||||
<table class="overview" cellspacing="0">
|
||||
<tr class ="overview">
|
||||
<th class="overview">ID</th>
|
||||
<th class="overview">Name</th>
|
||||
<th class="overview">Owner</th>
|
||||
<th class="overview"></th>
|
||||
</tr>
|
||||
{{range .Items}}
|
||||
<tr class="overview">
|
||||
<td>{{.ID}}</td>
|
||||
<td>{{.Name}}</td>
|
||||
<td>{{.Owner}}</td>
|
||||
<td><input type="checkbox" id="ID_{{.ID}}" name="name_{{.ID}}" value="value_{{.ID}}" checked></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
7957
playlist.json
Normal file
7957
playlist.json
Normal file
File diff suppressed because it is too large
Load Diff
7916
playlists_tracks.json
Normal file
7916
playlists_tracks.json
Normal file
File diff suppressed because it is too large
Load Diff
296
spotify-structs.go
Normal file
296
spotify-structs.go
Normal file
@ -0,0 +1,296 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type spotifyTrack struct {
|
||||
Album struct {
|
||||
AlbumType string `json:"album_type"`
|
||||
Artists []struct {
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"artists"`
|
||||
AvailableMarkets []string `json:"available_markets"`
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Images []struct {
|
||||
Height int `json:"height"`
|
||||
URL string `json:"url"`
|
||||
Width int `json:"width"`
|
||||
} `json:"images"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"album"`
|
||||
Artists []struct {
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"artists"`
|
||||
AvailableMarkets []string `json:"available_markets"`
|
||||
DiscNumber int `json:"disc_number"`
|
||||
DurationMs int `json:"duration_ms"`
|
||||
Explicit bool `json:"explicit"`
|
||||
ExternalIds struct {
|
||||
Isrc string `json:"isrc"`
|
||||
} `json:"external_ids"`
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Popularity int `json:"popularity"`
|
||||
PreviewURL string `json:"preview_url"`
|
||||
TrackNumber int `json:"track_number"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
IsLocal bool `json:"is_local"`
|
||||
}
|
||||
|
||||
type spotifyPlaylist struct {
|
||||
Collaborative bool `json:"collaborative"`
|
||||
Description interface{} `json:"description"`
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Followers struct {
|
||||
Href interface{} `json:"href"`
|
||||
Total int `json:"total"`
|
||||
} `json:"followers"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Images []struct {
|
||||
Height int `json:"height"`
|
||||
URL string `json:"url"`
|
||||
Width int `json:"width"`
|
||||
} `json:"images"`
|
||||
Name string `json:"name"`
|
||||
Owner struct {
|
||||
DisplayName interface{} `json:"display_name"`
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"owner"`
|
||||
Public bool `json:"public"`
|
||||
SnapshotID string `json:"snapshot_id"`
|
||||
Tracks struct {
|
||||
Href string `json:"href"`
|
||||
Items []struct {
|
||||
AddedAt time.Time `json:"added_at"`
|
||||
AddedBy struct {
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"added_by"`
|
||||
IsLocal bool `json:"is_local"`
|
||||
Track struct {
|
||||
Album struct {
|
||||
AlbumType string `json:"album_type"`
|
||||
Artists []struct {
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"artists"`
|
||||
AvailableMarkets []interface{} `json:"available_markets"`
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Images []struct {
|
||||
Height int `json:"height"`
|
||||
URL string `json:"url"`
|
||||
Width int `json:"width"`
|
||||
} `json:"images"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"album"`
|
||||
Artists []struct {
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"artists"`
|
||||
AvailableMarkets []interface{} `json:"available_markets"`
|
||||
DiscNumber int `json:"disc_number"`
|
||||
DurationMs int `json:"duration_ms"`
|
||||
Explicit bool `json:"explicit"`
|
||||
ExternalIds struct {
|
||||
Isrc string `json:"isrc"`
|
||||
} `json:"external_ids"`
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Popularity int `json:"popularity"`
|
||||
PreviewURL interface{} `json:"preview_url"`
|
||||
TrackNumber int `json:"track_number"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"track"`
|
||||
} `json:"items"`
|
||||
Limit int `json:"limit"`
|
||||
Next string `json:"next"`
|
||||
Offset int `json:"offset"`
|
||||
Previous interface{} `json:"previous"`
|
||||
Total int `json:"total"`
|
||||
} `json:"tracks"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
}
|
||||
|
||||
type spotifyPlaylistsOfUser struct {
|
||||
Href string `json:"href"`
|
||||
Items []struct {
|
||||
Collaborative bool `json:"collaborative"`
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Images []struct {
|
||||
Height int `json:"height"`
|
||||
URL string `json:"url"`
|
||||
Width int `json:"width"`
|
||||
} `json:"images"`
|
||||
Name string `json:"name"`
|
||||
Owner struct {
|
||||
DisplayName interface{} `json:"display_name"`
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"owner"`
|
||||
Public bool `json:"public"`
|
||||
SnapshotID string `json:"snapshot_id"`
|
||||
Tracks struct {
|
||||
Href string `json:"href"`
|
||||
Total int `json:"total"`
|
||||
} `json:"tracks"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"items"`
|
||||
Limit int `json:"limit"`
|
||||
Next *string `json:"next"`
|
||||
Offset int `json:"offset"`
|
||||
Previous interface{} `json:"previous"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type spotifyPlaylistsTracks struct {
|
||||
Href string `json:"href"`
|
||||
Items []struct {
|
||||
AddedAt time.Time `json:"added_at"`
|
||||
AddedBy struct {
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"added_by"`
|
||||
IsLocal bool `json:"is_local"`
|
||||
Track struct {
|
||||
Album struct {
|
||||
AlbumType string `json:"album_type"`
|
||||
Artists []struct {
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"artists"`
|
||||
AvailableMarkets []interface{} `json:"available_markets"`
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Images []struct {
|
||||
Height int `json:"height"`
|
||||
URL string `json:"url"`
|
||||
Width int `json:"width"`
|
||||
} `json:"images"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"album"`
|
||||
Artists []struct {
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"artists"`
|
||||
AvailableMarkets []interface{} `json:"available_markets"`
|
||||
DiscNumber int `json:"disc_number"`
|
||||
DurationMs int `json:"duration_ms"`
|
||||
Explicit bool `json:"explicit"`
|
||||
ExternalIds struct {
|
||||
Isrc string `json:"isrc"`
|
||||
} `json:"external_ids"`
|
||||
ExternalUrls struct {
|
||||
Spotify string `json:"spotify"`
|
||||
} `json:"external_urls"`
|
||||
Href string `json:"href"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Popularity int `json:"popularity"`
|
||||
PreviewURL interface{} `json:"preview_url"`
|
||||
TrackNumber int `json:"track_number"`
|
||||
Type string `json:"type"`
|
||||
URI string `json:"uri"`
|
||||
} `json:"track"`
|
||||
} `json:"items"`
|
||||
Limit int `json:"limit"`
|
||||
Next *string `json:"next"`
|
||||
Offset int `json:"offset"`
|
||||
Previous *string `json:"previous"`
|
||||
Total int `json:"total"`
|
||||
}
|
167
spotify.go
Normal file
167
spotify.go
Normal file
@ -0,0 +1,167 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
// "log"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"github.com/zmb3/spotify"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
const baseURL_track = "https://api.spotify.com/v1/tracks/"
|
||||
const baseURL_playlist = "https://api.spotify.com/v1/playlists/"
|
||||
const baseURL_playlists = "https://api.spotify.com/v1/me/playlists"
|
||||
|
||||
func get_spotify_auth() (auth spotify.Authenticator){
|
||||
auth = spotify.NewAuthenticator(redirectURI, spotify.ScopeUserReadPrivate, spotify.ScopePlaylistReadPrivate, spotify.ScopePlaylistReadCollaborative, spotify.ScopeUserLibraryRead)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: write this fkn func
|
||||
func check_if_track_is_valid(track_id string) (state bool) {
|
||||
state = true
|
||||
|
||||
// url := baseURL_track + track_id
|
||||
|
||||
// httpClient := &http.Client{}
|
||||
// req, _ := http.NewRequest("GET", url, nil)
|
||||
// req.Header.Set("Authorization", "Bearer "+oauthToken)
|
||||
// res, _ := httpClient.Do(req)
|
||||
|
||||
// body, _ := ioutil.ReadAll(res.Body)
|
||||
// data := new(spotifyTrack)
|
||||
// json.Unmarshal(body, &data)
|
||||
|
||||
// if data.IsLocal {
|
||||
// state = false
|
||||
// }
|
||||
return state
|
||||
}
|
||||
|
||||
func download_all_playlist_of_user(owner_id string, folder string) {
|
||||
url := baseURL_playlists + "?limit=50&offset=0"
|
||||
|
||||
httpClient := &http.Client{}
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+oauthToken)
|
||||
res, _ := httpClient.Do(req)
|
||||
|
||||
body, _ := ioutil.ReadAll(res.Body)
|
||||
data := new(spotifyPlaylistsOfUser)
|
||||
json.Unmarshal(body, &data)
|
||||
var counter = 0
|
||||
|
||||
for _,playlist := range data.Items {
|
||||
fmt.Printf("Downloading Playlist => %s <=\n",playlist.Name)
|
||||
download_playlist_to_folder(playlist.ID,playlist.Owner.ID,folder)
|
||||
}
|
||||
if data.Total > 50 {
|
||||
for(data.Next != nil) {
|
||||
nextUrl := *data.Next
|
||||
request, _ := http.NewRequest("GET",nextUrl, nil)
|
||||
request.Header.Set("Authorization", "Bearer "+oauthToken)
|
||||
response, _ := httpClient.Do(request)
|
||||
|
||||
body, _ = ioutil.ReadAll(response.Body)
|
||||
json.Unmarshal(body, &data)
|
||||
for _,playlist := range data.Items {
|
||||
counter++
|
||||
fmt.Printf("Downloading Playlist: => %s <=\n",playlist.Name)
|
||||
download_playlist_to_folder(playlist.ID,playlist.Owner.ID,folder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func download_playlist_to_folder(playlist_id string, owner_id string, folder string) {
|
||||
playlist_name := get_name_of_playlist(playlist_id,owner_id)
|
||||
|
||||
url := baseURL_playlist + playlist_id + "/tracks?limit=100&offset=0"
|
||||
|
||||
httpClient := &http.Client{}
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+oauthToken)
|
||||
res, _ := httpClient.Do(req)
|
||||
|
||||
body, _ := ioutil.ReadAll(res.Body)
|
||||
data := new(spotifyPlaylistsTracks)
|
||||
json.Unmarshal(body, &data)
|
||||
folder = folder + playlist_name+"/"
|
||||
var counter = 0
|
||||
|
||||
for _,track := range data.Items {
|
||||
counter++
|
||||
fmt.Println("Nr: ",counter," | Title: ",track.Track.Name," | Artist: ",track.Track.Artists[0].Name)
|
||||
if track.Track.ID == "" {
|
||||
// TODO: add file to manual_download list if no spotify id exist
|
||||
fmt.Printf("track '%s - %s' is a local file. please download this track manually",track.Track.Artists[0].Name,track.Track.Name)
|
||||
} else {
|
||||
download_track_to_folder(track.Track.ID,folder)
|
||||
}
|
||||
}
|
||||
|
||||
if data.Total > 100 {
|
||||
for(data.Next != nil) {
|
||||
nextUrl := *data.Next
|
||||
request, _ := http.NewRequest("GET",nextUrl, nil)
|
||||
request.Header.Set("Authorization", "Bearer "+oauthToken)
|
||||
response, _ := httpClient.Do(request)
|
||||
|
||||
body, _ = ioutil.ReadAll(response.Body)
|
||||
json.Unmarshal(body, &data)
|
||||
for _,track := range data.Items {
|
||||
counter++
|
||||
fmt.Println("Nr: ",counter," | Title: ",track.Track.Name," | Artist: ",track.Track.Artists[0].Name)
|
||||
if track.Track.ID == "" {
|
||||
// TODO: add file to manual_download list if no spotify id exist
|
||||
fmt.Printf("track '%s - %s' is a local file. please download this track manually\n",track.Track.Artists[0].Name,track.Track.Name)
|
||||
} else {
|
||||
download_track_to_folder(track.Track.ID,folder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println("--------------------")
|
||||
}
|
||||
|
||||
func get_name_of_playlist(playlist_id string, owner_id string) (name string) {
|
||||
url := baseURL_playlist + playlist_id
|
||||
|
||||
httpClient := &http.Client{}
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+oauthToken)
|
||||
res, _ := httpClient.Do(req)
|
||||
|
||||
body, _ := ioutil.ReadAll(res.Body)
|
||||
data := new(spotifyPlaylist)
|
||||
json.Unmarshal(body, &data)
|
||||
|
||||
name = data.Name
|
||||
return
|
||||
}
|
||||
|
||||
func download_track_to_folder(track_id string, folder string) {
|
||||
track_name, artist_name := get_track_and_artist_name(track_id)
|
||||
filename := track_name+" - "+artist_name
|
||||
youtube_url := search_on_youtube(filename)
|
||||
download_mp3_from_youtube(youtube_url,folder,filename)
|
||||
}
|
||||
|
||||
// TODO: return error on failure
|
||||
func get_track_and_artist_name(track_id string) (track_name string, artist_name string){
|
||||
url := baseURL_track + track_id + "?market=DE"
|
||||
|
||||
httpClient := &http.Client{}
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+oauthToken)
|
||||
res, _ := httpClient.Do(req)
|
||||
|
||||
body, _ := ioutil.ReadAll(res.Body)
|
||||
data := new(spotifyTrack)
|
||||
json.Unmarshal(body, &data)
|
||||
|
||||
return data.Name, data.Artists[0].Name
|
||||
}
|
||||
|
71
static/tables.css
Normal file
71
static/tables.css
Normal file
@ -0,0 +1,71 @@
|
||||
/* jailOverview */
|
||||
table.overview {
|
||||
overflow:hidden;
|
||||
border:2px solid #d3d3d3;
|
||||
width:100%;
|
||||
margin:0% auto 0;
|
||||
-moz-border-radius:9px; /* FF1+ */
|
||||
-webkit-border-radius:5px; /* Saf3-4 */
|
||||
border-radius:9px;
|
||||
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
tr.overview:nth-child(even) {background: #CCC}
|
||||
th.overview {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* jailDetails */
|
||||
table.details {
|
||||
overflow:hidden;
|
||||
border:1px solid #d3d3d3;
|
||||
width:25%;
|
||||
margin:2% 0;
|
||||
-moz-border-radius:9px; /* FF1+ */
|
||||
-webkit-border-radius:5px; /* Saf3-4 */
|
||||
border-radius:9px;
|
||||
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
table.state {
|
||||
display: block;
|
||||
text-align: left;
|
||||
overflow:hidden;
|
||||
border:2px solid #d3d3d3;
|
||||
width:15%;
|
||||
margin-left: 50px;
|
||||
-moz-border-radius:9px; /* FF1+ */
|
||||
-webkit-border-radius:5px; /* Saf3-4 */
|
||||
border-radius:9px;
|
||||
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
|
||||
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
table.not-running {
|
||||
background: red;
|
||||
}
|
||||
table.running {
|
||||
background: green;
|
||||
}
|
||||
|
||||
table.details tr:hover {background-color: #f5f5f5}
|
||||
table.details th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.status-light {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
|
||||
margin-right: 10px;
|
||||
border-radius: 50%;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.status-light.up {
|
||||
background-color: #0b0;
|
||||
}
|
||||
.status-light.down {
|
||||
background-color: #b00;
|
||||
}
|
65
track.json
Normal file
65
track.json
Normal file
@ -0,0 +1,65 @@
|
||||
{
|
||||
"album" : {
|
||||
"album_type" : "album",
|
||||
"artists" : [ {
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/artist/0C0XlULifJtAgn6ZNCW2eu"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/artists/0C0XlULifJtAgn6ZNCW2eu",
|
||||
"id" : "0C0XlULifJtAgn6ZNCW2eu",
|
||||
"name" : "The Killers",
|
||||
"type" : "artist",
|
||||
"uri" : "spotify:artist:0C0XlULifJtAgn6ZNCW2eu"
|
||||
} ],
|
||||
"available_markets" : [ "AD", "AR", "AT", "AU", "BE", "BG", "BO", "BR", "CA", "CH", "CL", "CO", "CR", "CY", "CZ", "DE", "DK", "DO", "EC", "EE", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HU", "ID", "IE", "IS", "IT", "JP", "LI", "LT", "LU", "LV", "MC", "MT", "MX", "MY", "NI", "NL", "NO", "NZ", "PA", "PE", "PH", "PL", "PT", "PY", "SE", "SG", "SK", "SV", "TH", "TR", "TW", "US", "UY", "VN" ],
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/album/4OHNH3sDzIxnmUADXzv2kT"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/albums/4OHNH3sDzIxnmUADXzv2kT",
|
||||
"id" : "4OHNH3sDzIxnmUADXzv2kT",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/ac68a9e4a867ec3ce8249cd90a2d7c73755fb487",
|
||||
"width" : 629
|
||||
}, {
|
||||
"height" : 300,
|
||||
"url" : "https://i.scdn.co/image/d0186ad64df7d6fc5f65c20c7d16f4279ffeb815",
|
||||
"width" : 295
|
||||
}, {
|
||||
"height" : 64,
|
||||
"url" : "https://i.scdn.co/image/7c3ec33d478f5f517eeb5339c2f75f150e4d601e",
|
||||
"width" : 63
|
||||
} ],
|
||||
"name" : "Hot Fuss (Deluxe Version)",
|
||||
"type" : "album",
|
||||
"uri" : "spotify:album:4OHNH3sDzIxnmUADXzv2kT"
|
||||
},
|
||||
"artists" : [ {
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/artist/0C0XlULifJtAgn6ZNCW2eu"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/artists/0C0XlULifJtAgn6ZNCW2eu",
|
||||
"id" : "0C0XlULifJtAgn6ZNCW2eu",
|
||||
"name" : "The Killers",
|
||||
"type" : "artist",
|
||||
"uri" : "spotify:artist:0C0XlULifJtAgn6ZNCW2eu"
|
||||
} ],
|
||||
"available_markets" : [ "AD", "AR", "AT", "AU", "BE", "BG", "BO", "BR", "CA", "CH", "CL", "CO", "CR", "CY", "CZ", "DE", "DK", "DO", "EC", "EE", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HU", "ID", "IE", "IS", "IT", "JP", "LI", "LT", "LU", "LV", "MC", "MT", "MX", "MY", "NI", "NL", "NO", "NZ", "PA", "PE", "PH", "PL", "PT", "PY", "SE", "SG", "SK", "SV", "TH", "TR", "TW", "US", "UY", "VN" ],
|
||||
"disc_number" : 1,
|
||||
"duration_ms" : 222200,
|
||||
"explicit" : false,
|
||||
"external_ids" : {
|
||||
"isrc" : "GBFFP0300052"
|
||||
},
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/track/3n3Ppam7vgaVa1iaRUc9Lp"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/tracks/3n3Ppam7vgaVa1iaRUc9Lp",
|
||||
"id" : "3n3Ppam7vgaVa1iaRUc9Lp",
|
||||
"name" : "Mr. Brightside",
|
||||
"popularity" : 82,
|
||||
"preview_url" : "https://p.scdn.co/mp3-preview/4839b070015ab7d6de9fec1756e1f3096d908fba?cid=aebff49ecd9e446ba573989feda8cb05",
|
||||
"track_number" : 2,
|
||||
"type" : "track",
|
||||
"uri" : "spotify:track:3n3Ppam7vgaVa1iaRUc9Lp"
|
||||
}
|
669
user_playlists.json
Normal file
669
user_playlists.json
Normal file
@ -0,0 +1,669 @@
|
||||
{
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists?offset=0&limit=20",
|
||||
"items" : [ {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/5SakrMHEosUtR34BCvuIB4"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/5SakrMHEosUtR34BCvuIB4",
|
||||
"id" : "5SakrMHEosUtR34BCvuIB4",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/63cf47c5df6fa98c10fc26f1bb6c54e05761f961",
|
||||
"width" : 640
|
||||
} ],
|
||||
"name" : "Wermonster – Katalyst Sessions, Vol. 2",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "HrTm3haQXscVBeJEI2i2d0t6EqeY/VIYe6GNdbZSuOfjYd5fFQXmj5PjmsyLp8K8",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/5SakrMHEosUtR34BCvuIB4/tracks",
|
||||
"total" : 11
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:5SakrMHEosUtR34BCvuIB4"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/spudodud/playlist/2rJq9tx7Oq7ugp8LtIU1Sq"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/spudodud/playlists/2rJq9tx7Oq7ugp8LtIU1Sq",
|
||||
"id" : "2rJq9tx7Oq7ugp8LtIU1Sq",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://mosaic.scdn.co/640/34c7435de0f8bcec06fa4290fd995bd3a719b1b615718e786d040966ce6b14be51481f0440d7fa1a1204cb8c2590bc23a08213b489442ddc17392a91770de29f3912e2ac8b69dd61c05871e287bfe71f",
|
||||
"width" : 640
|
||||
}, {
|
||||
"height" : 300,
|
||||
"url" : "https://mosaic.scdn.co/300/34c7435de0f8bcec06fa4290fd995bd3a719b1b615718e786d040966ce6b14be51481f0440d7fa1a1204cb8c2590bc23a08213b489442ddc17392a91770de29f3912e2ac8b69dd61c05871e287bfe71f",
|
||||
"width" : 300
|
||||
}, {
|
||||
"height" : 60,
|
||||
"url" : "https://mosaic.scdn.co/60/34c7435de0f8bcec06fa4290fd995bd3a719b1b615718e786d040966ce6b14be51481f0440d7fa1a1204cb8c2590bc23a08213b489442ddc17392a91770de29f3912e2ac8b69dd61c05871e287bfe71f",
|
||||
"width" : 60
|
||||
} ],
|
||||
"name" : "Cabvno - F e e l i n g s",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/spudodud"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/spudodud",
|
||||
"id" : "spudodud",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:spudodud"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "LGfDqFtGvnlOnmWQDsJlPAPfPDx2ZCWeeKOUgySmZ3vpoZu5S+EuU7i15t0lqaUB",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/spudodud/playlists/2rJq9tx7Oq7ugp8LtIU1Sq/tracks",
|
||||
"total" : 16
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:spudodud:playlist:2rJq9tx7Oq7ugp8LtIU1Sq"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/1138694516/playlist/3ZeOTuO6V51syAUQ0SxRCr"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/1138694516/playlists/3ZeOTuO6V51syAUQ0SxRCr",
|
||||
"id" : "3ZeOTuO6V51syAUQ0SxRCr",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://mosaic.scdn.co/640/1a7fbb2c31ef7f97f29e5199d229fde047a7beea9b5be0773fe25bc5a34a66f237a08758528500aad19096594fe2d8a972629ac0fe1ecaa5f6c0b13594a13f0b0a4acd42bf2f8d72b5b0b9447235d6ce",
|
||||
"width" : 640
|
||||
}, {
|
||||
"height" : 300,
|
||||
"url" : "https://mosaic.scdn.co/300/1a7fbb2c31ef7f97f29e5199d229fde047a7beea9b5be0773fe25bc5a34a66f237a08758528500aad19096594fe2d8a972629ac0fe1ecaa5f6c0b13594a13f0b0a4acd42bf2f8d72b5b0b9447235d6ce",
|
||||
"width" : 300
|
||||
}, {
|
||||
"height" : 60,
|
||||
"url" : "https://mosaic.scdn.co/60/1a7fbb2c31ef7f97f29e5199d229fde047a7beea9b5be0773fe25bc5a34a66f237a08758528500aad19096594fe2d8a972629ac0fe1ecaa5f6c0b13594a13f0b0a4acd42bf2f8d72b5b0b9447235d6ce",
|
||||
"width" : 60
|
||||
} ],
|
||||
"name" : "Chaos Computer Club",
|
||||
"owner" : {
|
||||
"display_name" : "Max Jakob",
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/1138694516"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/1138694516",
|
||||
"id" : "1138694516",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:1138694516"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "yNg/Gg9EiK3sJaxWx5ir56snYMONwrPS3XnUS297nwvGmYGWOHMKzcVf8Zq1CdPw",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/1138694516/playlists/3ZeOTuO6V51syAUQ0SxRCr/tracks",
|
||||
"total" : 120
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:1138694516:playlist:3ZeOTuO6V51syAUQ0SxRCr"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/1NFtmluZwJwjSyYg01C4ci"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/1NFtmluZwJwjSyYg01C4ci",
|
||||
"id" : "1NFtmluZwJwjSyYg01C4ci",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/a7e3151a7f9d8b4acf6c60002d3ce51820a8a906",
|
||||
"width" : 640
|
||||
} ],
|
||||
"name" : "Monophonics — Sound of Sinning",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "kQ7tUJvphWbhapFWOV9wDVCoVE8BlBZqiBnTyaVUryNut7IbDjYKxsboqe3BT+P8",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/1NFtmluZwJwjSyYg01C4ci/tracks",
|
||||
"total" : 11
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:1NFtmluZwJwjSyYg01C4ci"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/spotify/playlist/37i9dQZF1E9KaZS06fvYeF"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/spotify/playlists/37i9dQZF1E9KaZS06fvYeF",
|
||||
"id" : "37i9dQZF1E9KaZS06fvYeF",
|
||||
"images" : [ {
|
||||
"height" : null,
|
||||
"url" : "https://lineup-images.scdn.co/your-top-songs-2017_DEFAULT-en.jpg",
|
||||
"width" : null
|
||||
} ],
|
||||
"name" : "Your Top Songs 2017",
|
||||
"owner" : {
|
||||
"display_name" : "Spotify",
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/spotify"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/spotify",
|
||||
"id" : "spotify",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:spotify"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "MAJs40+OEEDiN6WXWsV2+ULBssCJKIkQZP11ns+iOvy9nJK2YkqSv1wWhce0k8H/NdaY6iHEEns=",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/spotify/playlists/37i9dQZF1E9KaZS06fvYeF/tracks",
|
||||
"total" : 100
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:spotify:playlist:37i9dQZF1E9KaZS06fvYeF"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/0g6NEmZTsiiWTOqpiK0haQ"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/0g6NEmZTsiiWTOqpiK0haQ",
|
||||
"id" : "0g6NEmZTsiiWTOqpiK0haQ",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://mosaic.scdn.co/640/b2819b73e847485a96534c94f51d80d61762596480f4d7cd0dc9d0d095b469b698804c502baaf0bd95b83949bcbcf6391e1100239e73644e144edb0aa38fa4e806f58470a4c0a899f79f1c49e9299be0",
|
||||
"width" : 640
|
||||
}, {
|
||||
"height" : 300,
|
||||
"url" : "https://mosaic.scdn.co/300/b2819b73e847485a96534c94f51d80d61762596480f4d7cd0dc9d0d095b469b698804c502baaf0bd95b83949bcbcf6391e1100239e73644e144edb0aa38fa4e806f58470a4c0a899f79f1c49e9299be0",
|
||||
"width" : 300
|
||||
}, {
|
||||
"height" : 60,
|
||||
"url" : "https://mosaic.scdn.co/60/b2819b73e847485a96534c94f51d80d61762596480f4d7cd0dc9d0d095b469b698804c502baaf0bd95b83949bcbcf6391e1100239e73644e144edb0aa38fa4e806f58470a4c0a899f79f1c49e9299be0",
|
||||
"width" : 60
|
||||
} ],
|
||||
"name" : "The Ones That Got Away",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "ZtdUslCA0tFlmpKMeG/J+LUJ2BRqp1OUi/8pjddmY3uKeC5DUH44DPjkcmB8J+AL",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/0g6NEmZTsiiWTOqpiK0haQ/tracks",
|
||||
"total" : 30
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:0g6NEmZTsiiWTOqpiK0haQ"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/0lcf7RFedS7iASJwVNVO2z"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/0lcf7RFedS7iASJwVNVO2z",
|
||||
"id" : "0lcf7RFedS7iASJwVNVO2z",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/005b3e947c5496795a20168cd0de619fe44ad611",
|
||||
"width" : 640
|
||||
} ],
|
||||
"name" : "Spitkid – Punk Is Dad",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "aU3zrm4ciec16HfcIzb8e6j7vSuz58pH28H5qsjOEVLTTaJeIzorkQ4Er25nzxMl",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/0lcf7RFedS7iASJwVNVO2z/tracks",
|
||||
"total" : 5
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:0lcf7RFedS7iASJwVNVO2z"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/4hx02vTCeVkU5qOsfVKLUx"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/4hx02vTCeVkU5qOsfVKLUx",
|
||||
"id" : "4hx02vTCeVkU5qOsfVKLUx",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/b8bd07481c4a13efac013aba7f51d6fb55fa002b",
|
||||
"width" : 640
|
||||
} ],
|
||||
"name" : "6 Feet Beneath the Moon",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : true,
|
||||
"snapshot_id" : "3E0WEU+RYHwfoHTqOIiF+9rRfhqSotsJTk4tQTK9MFjOL+JtEZ86zfOH+B/Mm6Ns",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/4hx02vTCeVkU5qOsfVKLUx/tracks",
|
||||
"total" : 14
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:4hx02vTCeVkU5qOsfVKLUx"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/1138581523/playlist/6jlYVxyao58RK5R8Awvw4L"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/1138581523/playlists/6jlYVxyao58RK5R8Awvw4L",
|
||||
"id" : "6jlYVxyao58RK5R8Awvw4L",
|
||||
"images" : [ {
|
||||
"height" : null,
|
||||
"url" : "https://pl.scdn.co/images/pl/default/44c71263440bab6033b7b0e81c61a7c895a3f955",
|
||||
"width" : null
|
||||
} ],
|
||||
"name" : "Gott zieht alles.",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/1138581523"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/1138581523",
|
||||
"id" : "1138581523",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:1138581523"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "XCDqMhkHDlCcE4dGFjnuzZD1Lfw/97l0wkajPGq/Jh/QSWU942cak0r2ln026eXE",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/1138581523/playlists/6jlYVxyao58RK5R8Awvw4L/tracks",
|
||||
"total" : 505
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:1138581523:playlist:6jlYVxyao58RK5R8Awvw4L"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/chillhopmusic/playlist/74sUjcvpGfdOvCHvgzNEDO"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/chillhopmusic/playlists/74sUjcvpGfdOvCHvgzNEDO",
|
||||
"id" : "74sUjcvpGfdOvCHvgzNEDO",
|
||||
"images" : [ {
|
||||
"height" : null,
|
||||
"url" : "https://pl.scdn.co/images/pl/default/ceaa77e94addd83567eb2eafeea3db5b912f5aa8",
|
||||
"width" : null
|
||||
} ],
|
||||
"name" : "lofi hip hop beats [lo-fi hip hop] Chillhop Music \\ Chilledcow",
|
||||
"owner" : {
|
||||
"display_name" : "Chillhop Music",
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/chillhopmusic"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/chillhopmusic",
|
||||
"id" : "chillhopmusic",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:chillhopmusic"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "ZJ+886d2bJtcFeJKO4uk983aNF+gTBCX7PIA4xNPBb0aqQMkUxHR1Bhbgb4XD18n",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/chillhopmusic/playlists/74sUjcvpGfdOvCHvgzNEDO/tracks",
|
||||
"total" : 150
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:chillhopmusic:playlist:74sUjcvpGfdOvCHvgzNEDO"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/3ZNXU8xlURnX63R3KLTJZB"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/3ZNXU8xlURnX63R3KLTJZB",
|
||||
"id" : "3ZNXU8xlURnX63R3KLTJZB",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/2d8dd23437aaed8331fe93a7a944cf39ffd0dbf1",
|
||||
"width" : 640
|
||||
} ],
|
||||
"name" : "Lee Hazlewood – Lounge Legends: Lee Hazelwood",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "rUtjLfYGcy+AbCacQJF2Qh6oiDT2sUyb2tratprNKYvNjS+JP0FyQfrePFvno+Lx",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/3ZNXU8xlURnX63R3KLTJZB/tracks",
|
||||
"total" : 20
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:3ZNXU8xlURnX63R3KLTJZB"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/6xLSmobth6DhSBr5MgWxjF"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/6xLSmobth6DhSBr5MgWxjF",
|
||||
"id" : "6xLSmobth6DhSBr5MgWxjF",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/ea33d6ce252ca8c556c67ee500f5c2891dc0dcbc",
|
||||
"width" : 640
|
||||
} ],
|
||||
"name" : "Vini Vici – Future Classics",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "YeZbovTdyerZIqkefPE+1DwbngUf1GSn2fFv1Wo56W0oXMarj64tbx0sk1Zp4s2b",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/6xLSmobth6DhSBr5MgWxjF/tracks",
|
||||
"total" : 9
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:6xLSmobth6DhSBr5MgWxjF"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/7oSTKcnx9xzfkj7NaJYpAX"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/7oSTKcnx9xzfkj7NaJYpAX",
|
||||
"id" : "7oSTKcnx9xzfkj7NaJYpAX",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/d9b728446d892db1627ef5c1f1f6990d934e2517",
|
||||
"width" : 640
|
||||
} ],
|
||||
"name" : "Judas Priest – Painkiller",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "B1GAuizaIMuYqUVkEtNnWwk0YmvtZC9PKIxVmtIEUe2C+8aUFxym7XaleRJOatFk",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/7oSTKcnx9xzfkj7NaJYpAX/tracks",
|
||||
"total" : 12
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:7oSTKcnx9xzfkj7NaJYpAX"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/6kgeO8Mo44Kw6aNYkvc5Wq"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/6kgeO8Mo44Kw6aNYkvc5Wq",
|
||||
"id" : "6kgeO8Mo44Kw6aNYkvc5Wq",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/f526bf6ad7ba2907a95cf96db6e134f61d92bef0",
|
||||
"width" : 640
|
||||
} ],
|
||||
"name" : "Prong – Cleansing",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "2lnKxC0dD48eVJB/kYD9HwpRfGBpYRcMet6rzfTj80pb1amMY4JrwNv49OTDCJLn",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/6kgeO8Mo44Kw6aNYkvc5Wq/tracks",
|
||||
"total" : 12
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:6kgeO8Mo44Kw6aNYkvc5Wq"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/cahwilms/playlist/2aPPzVTFqvhTFkjVesKGF4"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/cahwilms/playlists/2aPPzVTFqvhTFkjVesKGF4",
|
||||
"id" : "2aPPzVTFqvhTFkjVesKGF4",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://mosaic.scdn.co/640/072567be42da35e4320d85b209e7008283af521f3ba36422dc3cc216c6c039d182a6237383360999da66ee97e80ceba8af4c04ff931a4ade9a97713c38d3737416634d1af503643ca6fc011a45322dab",
|
||||
"width" : 640
|
||||
}, {
|
||||
"height" : 300,
|
||||
"url" : "https://mosaic.scdn.co/300/072567be42da35e4320d85b209e7008283af521f3ba36422dc3cc216c6c039d182a6237383360999da66ee97e80ceba8af4c04ff931a4ade9a97713c38d3737416634d1af503643ca6fc011a45322dab",
|
||||
"width" : 300
|
||||
}, {
|
||||
"height" : 60,
|
||||
"url" : "https://mosaic.scdn.co/60/072567be42da35e4320d85b209e7008283af521f3ba36422dc3cc216c6c039d182a6237383360999da66ee97e80ceba8af4c04ff931a4ade9a97713c38d3737416634d1af503643ca6fc011a45322dab",
|
||||
"width" : 60
|
||||
} ],
|
||||
"name" : "Anders",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/cahwilms"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/cahwilms",
|
||||
"id" : "cahwilms",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:cahwilms"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "R2SlBvks37Ig91TgWHjgMgNG5OCGrUt6tyE27WW5V0Eyx90HCle/Bc+Iuahz2wwO",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/cahwilms/playlists/2aPPzVTFqvhTFkjVesKGF4/tracks",
|
||||
"total" : 36
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:cahwilms:playlist:2aPPzVTFqvhTFkjVesKGF4"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/4yzjygtb9g0s7RjTbGtykg"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/4yzjygtb9g0s7RjTbGtykg",
|
||||
"id" : "4yzjygtb9g0s7RjTbGtykg",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/fbba1f95f668d8a8189fd56a44bb8140965a4dcf",
|
||||
"width" : 640
|
||||
} ],
|
||||
"name" : "Limp Bizkit – Results May Vary",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "928IsyzrP7f95eI0MHIOzAJ5Y1O8re825AIIc+Pzd5r8NHaPlQMj/e+Gj8T9NwyA",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/4yzjygtb9g0s7RjTbGtykg/tracks",
|
||||
"total" : 18
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:4yzjygtb9g0s7RjTbGtykg"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/4VHuFikSCbrbsAOBNZMuc0"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/4VHuFikSCbrbsAOBNZMuc0",
|
||||
"id" : "4VHuFikSCbrbsAOBNZMuc0",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/995f6d56c0930a316f025e88f1b67d046fc785ce",
|
||||
"width" : 640
|
||||
} ],
|
||||
"name" : "Limp Bizkit – New Old Songs",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "IOqztjtE8QOBiAMcEpzg5Zc0iKRvVyPUCfzkRn8cgHksWxCRXeiEIricxG+ARbWp",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/4VHuFikSCbrbsAOBNZMuc0/tracks",
|
||||
"total" : 16
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:4VHuFikSCbrbsAOBNZMuc0"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/1166762945/playlist/16SVZnYC3bIffe8wzeec2g"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/1166762945/playlists/16SVZnYC3bIffe8wzeec2g",
|
||||
"id" : "16SVZnYC3bIffe8wzeec2g",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://mosaic.scdn.co/640/6d62004edf00a46a0a405e2a3e26f16f4758729ca096eb8d085c2ad3967684a3ce3e329e5ded731f6793b455c8cfc0852ee13bca5cb5e6ad575377d11f2c920d3f98d0d69608fddedaef133b43afe325",
|
||||
"width" : 640
|
||||
}, {
|
||||
"height" : 300,
|
||||
"url" : "https://mosaic.scdn.co/300/6d62004edf00a46a0a405e2a3e26f16f4758729ca096eb8d085c2ad3967684a3ce3e329e5ded731f6793b455c8cfc0852ee13bca5cb5e6ad575377d11f2c920d3f98d0d69608fddedaef133b43afe325",
|
||||
"width" : 300
|
||||
}, {
|
||||
"height" : 60,
|
||||
"url" : "https://mosaic.scdn.co/60/6d62004edf00a46a0a405e2a3e26f16f4758729ca096eb8d085c2ad3967684a3ce3e329e5ded731f6793b455c8cfc0852ee13bca5cb5e6ad575377d11f2c920d3f98d0d69608fddedaef133b43afe325",
|
||||
"width" : 60
|
||||
} ],
|
||||
"name" : "Hotline Miami 2 OST",
|
||||
"owner" : {
|
||||
"display_name" : "Szczepan Kozal",
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/1166762945"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/1166762945",
|
||||
"id" : "1166762945",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:1166762945"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "WJqWiq1eJI0UhSiPk2ZG962ijsgiFMrNmGkO87fW+vkVHjE5Y0eF6xDsj0kipSM0",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/1166762945/playlists/16SVZnYC3bIffe8wzeec2g/tracks",
|
||||
"total" : 40
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:1166762945:playlist:16SVZnYC3bIffe8wzeec2g"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/2ETuPRlZewzyOQqD2mRENg"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/2ETuPRlZewzyOQqD2mRENg",
|
||||
"id" : "2ETuPRlZewzyOQqD2mRENg",
|
||||
"images" : [ {
|
||||
"height" : 640,
|
||||
"url" : "https://i.scdn.co/image/b32b1124650dbd323cd111dbbccbb49be8e65057",
|
||||
"width" : 640
|
||||
} ],
|
||||
"name" : "winter / metals",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "Zc5+uJIE8K9YfulH3BOgmW//tNxWUyKBsz9qfjyzLuIZFXPLRFNTvX+xZttLYixT",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/2ETuPRlZewzyOQqD2mRENg/tracks",
|
||||
"total" : 1
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:2ETuPRlZewzyOQqD2mRENg"
|
||||
}, {
|
||||
"collaborative" : false,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno/playlist/7geF25MVtBAgrT2D5wZWCS"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/7geF25MVtBAgrT2D5wZWCS",
|
||||
"id" : "7geF25MVtBAgrT2D5wZWCS",
|
||||
"images" : [ {
|
||||
"height" : null,
|
||||
"url" : "https://pl.scdn.co/images/pl/default/49eb388d415f6b5765fa7f666ca2a8f9c68c05f0",
|
||||
"width" : null
|
||||
} ],
|
||||
"name" : "dark souls",
|
||||
"owner" : {
|
||||
"display_name" : null,
|
||||
"external_urls" : {
|
||||
"spotify" : "https://open.spotify.com/user/fireno"
|
||||
},
|
||||
"href" : "https://api.spotify.com/v1/users/fireno",
|
||||
"id" : "fireno",
|
||||
"type" : "user",
|
||||
"uri" : "spotify:user:fireno"
|
||||
},
|
||||
"public" : false,
|
||||
"snapshot_id" : "8cfsxUDwg1p9xjDNparGBtskV3Y88F9NpD3vtY9TbFEU0tdt8d4kTh1/Sav7zw56",
|
||||
"tracks" : {
|
||||
"href" : "https://api.spotify.com/v1/users/fireno/playlists/7geF25MVtBAgrT2D5wZWCS/tracks",
|
||||
"total" : 8
|
||||
},
|
||||
"type" : "playlist",
|
||||
"uri" : "spotify:user:fireno:playlist:7geF25MVtBAgrT2D5wZWCS"
|
||||
} ],
|
||||
"limit" : 20,
|
||||
"next" : "https://api.spotify.com/v1/users/fireno/playlists?offset=20&limit=20",
|
||||
"offset" : 0,
|
||||
"previous" : null,
|
||||
"total" : 71
|
||||
}
|
17
utils.go
Normal file
17
utils.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func waitForInput() {
|
||||
var dummy string
|
||||
_, _ = fmt.Scanln(&dummy)
|
||||
}
|
||||
|
||||
func cleanString(s string) (cleans string){
|
||||
reg, _ := regexp.Compile("[^a-zA-Z0-9 ]+")
|
||||
cleans = reg.ReplaceAllString(s, "")
|
||||
return
|
||||
}
|
107
web.go
Normal file
107
web.go
Normal file
@ -0,0 +1,107 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
// "os"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"html/template"
|
||||
"regexp"
|
||||
"io/ioutil"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
var templates = template.Must(template.ParseGlob("pages/*.html"))
|
||||
var validPath = regexp.MustCompile("^/(webclient)/(.*)$")
|
||||
|
||||
type Page struct {
|
||||
Title string
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
|
||||
// fmt.Println("Running makeHandler")
|
||||
// return func(w http.ResponseWriter, r *http.Request) {
|
||||
// m := validPath.FindStringSubmatch(r.URL.Path)
|
||||
// if m == nil {
|
||||
// http.NotFound(w, r)
|
||||
// return
|
||||
// }
|
||||
// fmt.Println("makeHandler::request::" + r.RequestURI)
|
||||
// fn(w, r, m[2])
|
||||
// }
|
||||
// }
|
||||
|
||||
type testStructForWeb struct {
|
||||
ID string
|
||||
Name string
|
||||
Owner string
|
||||
}
|
||||
|
||||
type Context struct {
|
||||
Items []testStructForWeb
|
||||
}
|
||||
|
||||
func serveSingle(pattern string, filename string) {
|
||||
http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, filename)
|
||||
})
|
||||
}
|
||||
|
||||
func webclientHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("webclientHandler called")
|
||||
|
||||
var result []testStructForWeb
|
||||
|
||||
url := baseURL_playlists + "?limit=50&offset=0"
|
||||
|
||||
httpClient := &http.Client{}
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
req.Header.Set("Authorization", "Bearer "+oauthToken)
|
||||
res, _ := httpClient.Do(req)
|
||||
|
||||
body, _ := ioutil.ReadAll(res.Body)
|
||||
data := new(spotifyPlaylistsOfUser)
|
||||
json.Unmarshal(body, &data)
|
||||
var counter = 0
|
||||
|
||||
for _,playlist := range data.Items {
|
||||
counter++
|
||||
result = append(result, testStructForWeb{ID: playlist.ID, Name: playlist.Name, Owner: (playlist.Owner.ID)})
|
||||
}
|
||||
if data.Total > 50 {
|
||||
for(data.Next != nil) {
|
||||
nextUrl := *data.Next
|
||||
request, _ := http.NewRequest("GET",nextUrl, nil)
|
||||
request.Header.Set("Authorization", "Bearer "+oauthToken)
|
||||
response, _ := httpClient.Do(request)
|
||||
|
||||
body, _ = ioutil.ReadAll(response.Body)
|
||||
json.Unmarshal(body, &data)
|
||||
for _,playlist := range data.Items {
|
||||
counter++
|
||||
result = append(result, testStructForWeb{ID: playlist.ID, Name: playlist.Name, Owner: playlist.Owner.ID})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
playlists := Context{Items: result}
|
||||
|
||||
templates.ExecuteTemplate(w, "playlists", playlists)
|
||||
}
|
||||
|
||||
func submitDownloadQueueHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// for playlist_id in all_playlists
|
||||
// if r.PostFormValue(playlist_id)
|
||||
// end for
|
||||
}
|
||||
|
||||
func startWebServer() {
|
||||
http.HandleFunc("/webclient/", webclientHandler)
|
||||
http.HandleFunc("/submitDownloadQueue/", submitDownloadQueueHandler)
|
||||
|
||||
serveSingle("/static/tables.css", "./static/tables.css")
|
||||
|
||||
fmt.Println("http://localhost:8088/webclient")
|
||||
http.ListenAndServe("localhost:8088", nil)
|
||||
}
|
26
youtube-dl.go
Normal file
26
youtube-dl.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
// "fmt"
|
||||
// "net/http"
|
||||
// "net/url"
|
||||
// "strings"
|
||||
)
|
||||
|
||||
|
||||
// TODO: make POST url global configurable
|
||||
func download_mp3_from_youtube(youtube_url string, folder string, filename string) {
|
||||
if (youtube_url != "" && folder != "" && filename != "") {
|
||||
// form := url.Values{}
|
||||
// form.Add("url", youtube_url)
|
||||
// form.Add("folder", folder)
|
||||
// form.Add("filename", filename)
|
||||
|
||||
// httpClient := &http.Client{}
|
||||
// req, _ := http.NewRequest("POST", "http://127.0.0.1:1234/work", strings.NewReader(form.Encode()))
|
||||
// _, err := httpClient.Do(req)
|
||||
// if err != nil {
|
||||
// fmt.Printf("Command finished with error: %v", err)
|
||||
// }
|
||||
}
|
||||
}
|
49
youtube-structs.go
Normal file
49
youtube-structs.go
Normal file
@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import(
|
||||
"time"
|
||||
)
|
||||
|
||||
type youtubeSearchResult struct {
|
||||
Kind string `json:"kind"`
|
||||
Etag string `json:"etag"`
|
||||
NextPageToken string `json:"nextPageToken"`
|
||||
RegionCode string `json:"regionCode"`
|
||||
PageInfo struct {
|
||||
TotalResults int `json:"totalResults"`
|
||||
ResultsPerPage int `json:"resultsPerPage"`
|
||||
} `json:"pageInfo"`
|
||||
Items []struct {
|
||||
Kind string `json:"kind"`
|
||||
Etag string `json:"etag"`
|
||||
ID struct {
|
||||
Kind string `json:"kind"`
|
||||
VideoID string `json:"videoId"`
|
||||
} `json:"id"`
|
||||
Snippet struct {
|
||||
PublishedAt time.Time `json:"publishedAt"`
|
||||
ChannelID string `json:"channelId"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Thumbnails struct {
|
||||
Default struct {
|
||||
URL string `json:"url"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
} `json:"default"`
|
||||
Medium struct {
|
||||
URL string `json:"url"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
} `json:"medium"`
|
||||
High struct {
|
||||
URL string `json:"url"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
} `json:"high"`
|
||||
} `json:"thumbnails"`
|
||||
ChannelTitle string `json:"channelTitle"`
|
||||
LiveBroadcastContent string `json:"liveBroadcastContent"`
|
||||
} `json:"snippet"`
|
||||
} `json:"items"`
|
||||
}
|
58
youtube.go
Normal file
58
youtube.go
Normal file
@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import(
|
||||
"fmt"
|
||||
"log"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var youtubeToken = os.Getenv("YOUTUBE_TOKEN")
|
||||
var youtube_api = "https://www.googleapis.com/youtube/v3/search"
|
||||
var youtube_video_baseurl = "https://youtube.com/watch?v="
|
||||
|
||||
func search_on_youtube(searchString string) (youtube_url string) {
|
||||
reg, _ := regexp.Compile("[^a-zA-Z0-9 -]+")
|
||||
processedString := reg.ReplaceAllString(searchString, "")
|
||||
t := &url.URL{Path: processedString}
|
||||
http_request := t.String()
|
||||
http_request_encoded := (youtube_api+"?part=snippet&maxResults=1&q="+http_request+"&key="+youtubeToken)
|
||||
|
||||
response, err := http.Get(http_request_encoded)
|
||||
if err != nil {
|
||||
fmt.Printf("%s", err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
defer response.Body.Close()
|
||||
contents, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
fmt.Printf("%s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
data := new(youtubeSearchResult)
|
||||
json.Unmarshal(contents, &data)
|
||||
|
||||
if len(data.Items) > 0 {
|
||||
youtube_url = youtube_video_baseurl + data.Items[0].ID.VideoID
|
||||
} else {
|
||||
// TODO: put this spotify track on 'manual donwload queue'
|
||||
// youtube_url = "could not find track on yt"
|
||||
f, err := os.OpenFile("manual-download.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if _, err := f.Write([]byte(searchString+"\n")); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
Loading…
Reference in New Issue
Block a user