Skip to content

Commit

Permalink
Lastfm Search (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
WTFKr0 authored Nov 27, 2017
1 parent 168d371 commit 0f17bb3
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 11 deletions.
100 changes: 90 additions & 10 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ package main

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
golog "log"
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/streamlist/streamlist/internal/archiver"
"github.com/streamlist/streamlist/internal/youtube"
Expand Down Expand Up @@ -53,6 +57,11 @@ type response struct {
QueuedMedias []*Media

Youtubes []youtube.Video

LastFMEnabled bool
ArtistsList []lastFMArtist
AlbumsList []lastFMAlbum
TracksList []lastFMTrack
}

func stringInSlice(a string, list []string) bool {
Expand All @@ -72,16 +81,17 @@ func newResponse(r *http.Request, ps httprouter.Params) *response {
user, _, _ := r.BasicAuth()
isAdmin := stringInSlice(user, httpAdminUsers)
return &response{
Config: config.Get(),
Request: r,
Params: &ps,
User: ps.ByName("user"),
IsAdmin: isAdmin,
HTTPHost: httpHost,
Version: version,
Backlink: backlink,
DiskInfo: diskInfo,
Archiver: archive,
Config: config.Get(),
Request: r,
Params: &ps,
User: ps.ByName("user"),
IsAdmin: isAdmin,
HTTPHost: httpHost,
Version: version,
Backlink: backlink,
DiskInfo: diskInfo,
Archiver: archive,
LastFMEnabled: lastfmAPIKey != "",
}
}

Expand Down Expand Up @@ -163,6 +173,24 @@ func importHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params)
html(w, "import.html", res)
}

func searchHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var artists []lastFMArtist
var albums []lastFMAlbum
var tracks []lastFMTrack
if query := strings.TrimSpace(r.FormValue("q")); query != "" {
artists = searchArtists(query)
albums = searchAlbums(query)
tracks = searchTracks(query)
}

res := newResponse(r, ps)
res.Section = "search"
res.ArtistsList = artists
res.AlbumsList = albums
res.TracksList = tracks
html(w, "search.html", res)
}

func library(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
medias, err := ListMedias()
if err != nil {
Expand Down Expand Up @@ -574,3 +602,55 @@ func v1status(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprintf(w, "%s\n", status)
}

func getURL(url string) []byte {
fmt.Println("I GET:" + url)
client := &http.Client{
Timeout: time.Second * 2, // Maximum of 2 secs
}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
golog.Fatal(err)
}
req.Header.Set("User-Agent", "streamlist")
res, getErr := client.Do(req)
if getErr != nil {
golog.Fatal(getErr)
}
defer res.Body.Close()
body, readErr := ioutil.ReadAll(res.Body)
if readErr != nil {
golog.Fatal(readErr)
}
return body
}

func searchArtists(query string) []lastFMArtist {
url := "http://ws.audioscrobbler.com/2.0/?method=artist.search&artist=" + query + "&api_key=" + lastfmAPIKey + "&format=json"
body := getURL(url)

var result lastFMArtistsResponse
json.Unmarshal([]byte(body), &result)

return result.Results.ArtistMatches.Artist
}

func searchAlbums(query string) []lastFMAlbum {
url := "http://ws.audioscrobbler.com/2.0/?method=album.search&album=" + query + "&api_key=" + lastfmAPIKey + "&format=json"
body := getURL(url)

var result lastFMAlbumResponse
json.Unmarshal([]byte(body), &result)

return result.Results.AlbumMatches.Album
}

func searchTracks(query string) []lastFMTrack {
url := "http://ws.audioscrobbler.com/2.0/?method=track.search&track=" + query + "&api_key=" + lastfmAPIKey + "&format=json"
body := getURL(url)

var result lastFMTrackResponse
json.Unmarshal([]byte(body), &result)

return result.Results.TrackMatches.Track
}
7 changes: 7 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ var (

// version
version string

// others global vars
lastfmAPIKey string
)

func init() {
Expand All @@ -73,6 +76,7 @@ func init() {
cli.BoolVar(&letsencrypt, "letsencrypt", false, "enable TLS using Let's Encrypt")
cli.StringVar(&reverseProxyAuthHeader, "reverse-proxy-header", "X-Authenticated-User", "reverse proxy auth header")
cli.StringVar(&reverseProxyAuthIP, "reverse-proxy-ip", "", "reverse proxy auth IP")
lastfmAPIKey = os.Getenv("LASTFM_API_KEY")
}

func main() {
Expand Down Expand Up @@ -209,6 +213,9 @@ func main() {
// Import
r.GET(prefix("/import"), log(auth(importHandler, "admin")))

// Search
r.GET(prefix("/search"), log(auth(searchHandler, "admin")))

// Archiver
r.GET(prefix("/archiver/jobs"), auth(archiverJobs, "admin"))
r.POST(prefix("/archiver/save/:id"), log(auth(archiverSave, "admin")))
Expand Down
48 changes: 48 additions & 0 deletions streamlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,3 +401,51 @@ func listLists() ([]*List, error) {
db.Find(&lists)
return lists, db.Error
}

type lastFMArtistsResponse struct {
Results struct {
ArtistMatches struct {
Artist []lastFMArtist `json:"artist"`
}
}
}

type lastFMAlbumResponse struct {
Results struct {
AlbumMatches struct {
Album []lastFMAlbum `json:"album"`
}
}
}

type lastFMTrackResponse struct {
Results struct {
TrackMatches struct {
Track []lastFMTrack `json:"track"`
}
}
}

type lastFMTrack struct {
Name string `json:"name"`
Artist string `json:"artist"`
Image []lastFMImage `json:"image"`
Listeners string `json:"listeners"`
}

type lastFMAlbum struct {
Name string `json:"name"`
Artist string `json:"artist"`
Image []lastFMImage `json:"image"`
}

type lastFMArtist struct {
Name string `json:"name"`
Listeners string `json:"listeners"`
Image []lastFMImage `json:"image"`
}

type lastFMImage struct {
Text string `json:"#text"`
Size string `json:"size"`
}
3 changes: 3 additions & 0 deletions templates/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
Library
</a>
{{if $.IsAdmin}}
<a class="item {{if eq $.Section "search"}}{{end}}" href="/streamlist/search">
Search
</a>
<a class="item {{if eq $.Section "import"}}{{end}}" href="/streamlist/import">
Import
</a>
Expand Down
2 changes: 1 addition & 1 deletion templates/import.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ <h3 class="ui header">YouTube</h3>

<form class="ui large form" action="/streamlist/import" method="GET">
<div class="sixteen wide field">
<input type="text" name="q" placeholder="Search" value="" autofocus="autofocus" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
<input type="text" name="q" placeholder="Search" value="{{$query}}" autofocus="autofocus" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
</div>
<!--div class="sixteen wide field">
<div class="ui hidden divider"></div>
Expand Down
99 changes: 99 additions & 0 deletions templates/search.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
{{template "header.html" .}}

{{$query := $.Request.FormValue "q"}}

<div class="ui container">
<h2 class="ui header">Search</h2>
</div>

<div class="ui hidden divider"></div>

<div id="jobs" class="ui container">
</div>

<div class="ui hidden divider"></div>

<div class="ui container">
<h3 class="ui header">Search for artists / albums / tracks</h3>

<form class="ui large form" action="/streamlist/search" method="GET">
<div class="sixteen wide field">
<input type="text" name="q" placeholder="Search" value="{{$query}}" autofocus="autofocus" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
</div>
<!--div class="sixteen wide field">
<div class="ui hidden divider"></div>
<button type="submit" class="ui right floated blue button">Search</button>
</div-->
</form>

{{if $query}}
<div class="ui hidden divider"></div>
<h5 class="ui header">
Results for "{{$query}}"
</h5>

{{if not $.LastFMEnabled}}
<p>Please set LAST FM Api Key to get results here</p>
{{end}}

<div class="ui three column grid">
<div class="column">
<p>Artists</p>
{{if $.ArtistsList}}
<table class="ui celled table">
<tbody>
{{range $artist := $.ArtistsList}}
<tr>
<td class="selectable ten wide">
<a href='import?q={{$artist.Name}}'>{{$artist.Name}}</a>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
</div>
<div class="column">
<p>Albums</p>
{{if $.AlbumsList}}
<table class="ui celled table">
<tbody>
{{range $album := $.AlbumsList}}
<tr>
<td class="selectable ten wide">
<a href='import?q={{$album.Artist}} {{$album.Name}} full album'>{{$album.Name}}</a>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
</div>
<div class="column">
<p>Tracks</p>
{{if $.TracksList}}
<table class="ui celled table">
<tbody>
{{range $track := $.TracksList}}
<tr>
<td class="selectable ten wide">
<a href='import?q={{$track.Artist}} {{$track.Name}}'>{{$track.Name}}</a>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
</div>
</div>
{{end}}
</div>

<script>
$(document).ready(function() {
poller('#jobs', '/streamlist/archiver/jobs', 2000);
});
</script>


{{template "footer.html" .}}

0 comments on commit 0f17bb3

Please sign in to comment.