Merge pull request #40 from qvalentin/feature/annotations
annotations support
This commit is contained in:
commit
98aa8db9ee
127
annotations.go
Normal file
127
annotations.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
type annotationsResponse struct {
|
||||||
|
Response struct {
|
||||||
|
Referent struct {
|
||||||
|
Annotations []struct {
|
||||||
|
Body struct {
|
||||||
|
Html string `json:"html"`
|
||||||
|
} `json:"body"`
|
||||||
|
} `json:"annotations"`
|
||||||
|
} `json:"referent"`
|
||||||
|
} `json:"response"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func annotationsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
|
||||||
|
if data, err := getCache(id); err == nil {
|
||||||
|
|
||||||
|
response, err := json.Marshal(data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("could not marshal json: %s\n", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
render("error", w, map[string]string{
|
||||||
|
"Status": "500",
|
||||||
|
"Error": "Could not parse genius api response",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("content-type", "application/json")
|
||||||
|
_, err = w.Write(response)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorln("Error sending response: ", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("https://genius.com/api/referents/%s?text_format=html", id)
|
||||||
|
resp, err := sendRequest(url)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorln(err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
render("error", w, map[string]string{
|
||||||
|
"Status": "500",
|
||||||
|
"Error": "cannot reach genius servers",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusNotFound {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
render("error", w, map[string]string{
|
||||||
|
"Status": "404",
|
||||||
|
"Error": "page not found",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err = buf.ReadFrom(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorln("Error paring genius api response", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
render("error", w, map[string]string{
|
||||||
|
"Status": "500",
|
||||||
|
"Error": "Parsing error",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var data annotationsResponse
|
||||||
|
err = json.Unmarshal(buf.Bytes(), &data)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("could not unmarshal json: %s\n", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
render("error", w, map[string]string{
|
||||||
|
"Status": "500",
|
||||||
|
"Error": "Could not parse genius api response",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("content-type", "application/json")
|
||||||
|
body := data.Response.Referent.Annotations[0].Body
|
||||||
|
body.Html = cleanBody(body.Html)
|
||||||
|
response, err := json.Marshal(body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("could not marshal json: %s\n", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
render("error", w, map[string]string{
|
||||||
|
"Status": "500",
|
||||||
|
"Error": "Could not parse genius api response",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setCache(id, body)
|
||||||
|
_, err = w.Write(response)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorln("Error sending response: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanBody(body string) string {
|
||||||
|
var withCleanedImageLinks = strings.Replace(body, "https://images.rapgenius.com/", "/images/", -1)
|
||||||
|
|
||||||
|
var re = regexp.MustCompile(`https?:\/\/[a-z]*.?genius.com`)
|
||||||
|
var withCleanedLinks = re.ReplaceAllString(withCleanedImageLinks, "")
|
||||||
|
|
||||||
|
return withCleanedLinks
|
||||||
|
}
|
1
main.go
1
main.go
@ -29,6 +29,7 @@ func main() {
|
|||||||
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { render("home", w, nil) })
|
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { render("home", w, nil) })
|
||||||
r.HandleFunc("/search", searchHandler).Methods("GET")
|
r.HandleFunc("/search", searchHandler).Methods("GET")
|
||||||
r.HandleFunc("/{id}-lyrics", lyricsHandler)
|
r.HandleFunc("/{id}-lyrics", lyricsHandler)
|
||||||
|
r.HandleFunc("/{id}/{artist-song}/{verse}/annotations", annotationsHandler)
|
||||||
r.HandleFunc("/images/{filename}.{ext}", proxyHandler)
|
r.HandleFunc("/images/{filename}.{ext}", proxyHandler)
|
||||||
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||||
r.HandleFunc("/albums/{artist}/{albumName}", albumHandler)
|
r.HandleFunc("/albums/{artist}/{albumName}", albumHandler)
|
||||||
|
@ -14,6 +14,24 @@ document.querySelectorAll("#lyrics a").forEach(item => {
|
|||||||
|
|
||||||
function getAnnotation(e) {
|
function getAnnotation(e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
//const uri = e.target.parentElement.getAttribute("href")
|
const uri = e.target.parentElement.getAttribute("href")
|
||||||
console.log("Annotations are not yet implemented!")
|
const presentAnnotation = document.getElementById(uri)
|
||||||
|
if (presentAnnotation) {
|
||||||
|
presentAnnotation.remove()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr = new XMLHttpRequest()
|
||||||
|
xhr.open("GET", uri + "/annotations")
|
||||||
|
xhr.send()
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if (this.readyState == 4 && this.status == 200) {
|
||||||
|
const parsedReponse = JSON.parse(this.responseText)
|
||||||
|
const annotationDiv = document.createElement('div');
|
||||||
|
annotationDiv.innerHTML = parsedReponse.html
|
||||||
|
annotationDiv.id = uri
|
||||||
|
annotationDiv.className = "annotation"
|
||||||
|
e.target.parentElement.insertAdjacentElement('afterend', annotationDiv)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,8 +56,7 @@ body {
|
|||||||
cursor: initial;
|
cursor: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** NOT YET IMPLEMENTED
|
#lyrics a, .annotation {
|
||||||
#lyrics a {
|
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
@ -65,7 +64,6 @@ body {
|
|||||||
#lyrics a:hover {
|
#lyrics a:hover {
|
||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
}
|
}
|
||||||
***/
|
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
background-color: #ffcd38;
|
background-color: #ffcd38;
|
||||||
@ -166,6 +164,19 @@ a {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.annotation {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annotation img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annotation ul {
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -337,6 +348,16 @@ footer a:hover {
|
|||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#lyrics a, .annotation {
|
||||||
|
background-color: #272d44;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#lyrics a:hover {
|
||||||
|
background-color: #32384f;
|
||||||
|
}
|
||||||
|
|
||||||
#metadata h1 {
|
#metadata h1 {
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user