diff --git a/annotations.go b/annotations.go new file mode 100644 index 0000000..f77d35c --- /dev/null +++ b/annotations.go @@ -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 +} diff --git a/main.go b/main.go index 9b8c582..69799f9 100644 --- a/main.go +++ b/main.go @@ -29,6 +29,7 @@ func main() { r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { render("home", w, nil) }) r.HandleFunc("/search", searchHandler).Methods("GET") r.HandleFunc("/{id}-lyrics", lyricsHandler) + r.HandleFunc("/{id}/{artist-song}/{verse}/annotations", annotationsHandler) r.HandleFunc("/images/{filename}.{ext}", proxyHandler) r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) r.HandleFunc("/albums/{artist}/{albumName}", albumHandler) diff --git a/static/script.js b/static/script.js index 1a45d35..327ae5a 100644 --- a/static/script.js +++ b/static/script.js @@ -14,6 +14,24 @@ document.querySelectorAll("#lyrics a").forEach(item => { function getAnnotation(e) { e.preventDefault() - //const uri = e.target.parentElement.getAttribute("href") - console.log("Annotations are not yet implemented!") + const uri = e.target.parentElement.getAttribute("href") + 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) + } + } } diff --git a/static/style.css b/static/style.css index 1ced795..530484d 100644 --- a/static/style.css +++ b/static/style.css @@ -56,8 +56,7 @@ body { cursor: initial; } -/*** NOT YET IMPLEMENTED -#lyrics a { +#lyrics a, .annotation { background-color: #ddd; color: inherit; } @@ -65,7 +64,6 @@ body { #lyrics a:hover { background-color: #ccc; } -***/ nav { background-color: #ffcd38; @@ -166,6 +164,19 @@ a { cursor: pointer; } +.annotation { + padding: 1rem; +} + +.annotation img { + max-width: 100%; + height: auto; +} + +.annotation ul { + padding-left: 1em; +} + .hidden { display: none; } @@ -337,6 +348,16 @@ footer a:hover { color: #ccc; } + #lyrics a, .annotation { + background-color: #272d44; + color: inherit; + } + + + #lyrics a:hover { + background-color: #32384f; + } + #metadata h1 { color: #ddd; }