Merge pull request #40 from qvalentin/feature/annotations

annotations support
This commit is contained in:
Achraf RRAMI 2024-01-19 19:33:11 +00:00 committed by GitHub
commit 98aa8db9ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 172 additions and 5 deletions

127
annotations.go Normal file
View 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
}

View File

@ -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)

View File

@ -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)
}
}
} }

View File

@ -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;
} }