chore: second attempt to sync branch with latest changes

This commit is contained in:
rramiachraf 2024-07-13 22:06:26 +01:00
parent e536fc8ea5
commit 3e50e52d5e
7 changed files with 0 additions and 628 deletions

146
album.go
View File

@ -1,146 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/PuerkitoBio/goquery"
"github.com/gorilla/mux"
)
type album struct {
Artist string
Name string
Image string
About [2]string
Tracks []Track
}
type Track struct {
Title string
Url string
}
type albumMetadata struct {
Album struct {
Id int `json:"id"`
Image string `json:"cover_art_thumbnail_url"`
Name string `json:"name"`
Description string `json:"description_preview"`
Artist `json:"artist"`
}
AlbumAppearances []AlbumAppearances `json:"album_appearances"`
}
type AlbumAppearances struct {
Id int `json:"id"`
TrackNumber int `json:"track_number"`
Song struct {
Title string `json:"title"`
Url string `json:"url"`
}
}
type Artist struct {
Name string `json:"name"`
}
func (a *album) parseAlbumData(doc *goquery.Document) {
pageMetadata, exists := doc.Find("meta[itemprop='page_data']").Attr("content")
if !exists {
return
}
var albumMetadataFromPage albumMetadata
err := json.Unmarshal([]byte(pageMetadata), &albumMetadataFromPage)
if err != nil {
logger.Errorln(err)
}
albumData := albumMetadataFromPage.Album
a.Artist = albumData.Artist.Name
a.Name = albumData.Name
a.Image = albumData.Image
a.About[0] = albumData.Description
a.About[1] = truncateText(albumData.Description)
for _, track := range albumMetadataFromPage.AlbumAppearances {
url := strings.Replace(track.Song.Url, "https://genius.com", "", -1)
a.Tracks = append(a.Tracks, Track{Title: track.Song.Title, Url: url})
}
}
func (a *album) parse(doc *goquery.Document) {
a.parseAlbumData(doc)
}
func albumHandler(w http.ResponseWriter, r *http.Request) {
artist := mux.Vars(r)["artist"]
albumName := mux.Vars(r)["albumName"]
id := fmt.Sprintf("%s/%s", artist, albumName)
if data, err := getCache(id); err == nil {
render("album", w, data)
return
}
url := fmt.Sprintf("https://genius.com/albums/%s/%s", artist, albumName)
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
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
logger.Errorln(err)
w.WriteHeader(http.StatusInternalServerError)
render("error", w, map[string]string{
"Status": "500",
"Error": "something went wrong",
})
return
}
cf := doc.Find(".cloudflare_content").Length()
if cf > 0 {
logger.Errorln("cloudflare got in the way")
render("error", w, map[string]string{
"Status": "500",
"Error": "damn cloudflare, issue #21 on GitHub",
})
return
}
var a album
a.parse(doc)
render("album", w, a)
err = setCache(id, a)
if err != nil {
logger.Errorln(err)
}
}

View File

@ -1,131 +0,0 @@
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
}
err = setCache(id, body)
if err != nil {
logger.Errorln(err)
}
_, 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
go.sum
View File

@ -50,4 +50,3 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

176
lyrics.go
View File

@ -1,176 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/PuerkitoBio/goquery"
"github.com/gorilla/mux"
)
type song struct {
Artist string
Title string
Image string
Lyrics string
Credits map[string]string
About [2]string
Album string
LinkToAlbum string
}
type songResponse struct {
Response struct {
Song struct {
ArtistNames string `json:"artist_names"`
Image string `json:"song_art_image_thumbnail_url"`
Title string
Description struct {
Plain string
}
Album struct {
Url string `json:"url"`
Name string `json:"name"`
}
CustomPerformances []customPerformance `json:"custom_performances"`
}
}
}
type customPerformance struct {
Label string
Artists []struct {
Name string
}
}
func (s *song) parseLyrics(doc *goquery.Document) {
doc.Find("[data-lyrics-container='true']").Each(func(i int, ss *goquery.Selection) {
h, err := ss.Html()
if err != nil {
logger.Errorln("unable to parse lyrics", err)
}
s.Lyrics += h
})
}
func (s *song) parseSongData(doc *goquery.Document) {
attr, exists := doc.Find("meta[property='twitter:app:url:iphone']").Attr("content")
if exists {
songID := strings.Replace(attr, "genius://songs/", "", 1)
u := fmt.Sprintf("https://genius.com/api/songs/%s?text_format=plain", songID)
res, err := sendRequest(u)
if err != nil {
logger.Errorln(err)
}
defer res.Body.Close()
var data songResponse
decoder := json.NewDecoder(res.Body)
err = decoder.Decode(&data)
if err != nil {
logger.Errorln(err)
}
songData := data.Response.Song
s.Artist = songData.ArtistNames
s.Image = songData.Image
s.Title = songData.Title
s.About[0] = songData.Description.Plain
s.About[1] = truncateText(songData.Description.Plain)
s.Credits = make(map[string]string)
s.Album = songData.Album.Name
s.LinkToAlbum = strings.Replace(songData.Album.Url, "https://genius.com", "", -1)
for _, perf := range songData.CustomPerformances {
var artists []string
for _, artist := range perf.Artists {
artists = append(artists, artist.Name)
}
s.Credits[perf.Label] = strings.Join(artists, ", ")
}
}
}
func truncateText(text string) string {
textArr := strings.Split(text, "")
if len(textArr) > 250 {
return strings.Join(textArr[0:250], "") + "..."
}
return text
}
func (s *song) parse(doc *goquery.Document) {
s.parseLyrics(doc)
s.parseSongData(doc)
}
func lyricsHandler(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["id"]
if data, err := getCache(id); err == nil {
render("lyrics", w, data)
return
}
url := fmt.Sprintf("https://genius.com/%s-lyrics", 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
}
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
logger.Errorln(err)
w.WriteHeader(http.StatusInternalServerError)
render("error", w, map[string]string{
"Status": "500",
"Error": "something went wrong",
})
return
}
cf := doc.Find(".cloudflare_content").Length()
if cf > 0 {
logger.Errorln("cloudflare got in the way")
render("error", w, map[string]string{
"Status": "500",
"Error": "damn cloudflare, issue #21 on GitHub",
})
return
}
var s song
s.parse(doc)
render("lyrics", w, s)
err = setCache(id, s)
if err != nil {
logger.Errorln(err)
}
}

33
main.go
View File

@ -1,11 +1,7 @@
package main
import (
<<<<<<< HEAD
"context"
=======
"embed"
>>>>>>> upstream/main
"fmt"
"net"
"net/http"
@ -22,36 +18,7 @@ import (
var staticFiles embed.FS
func main() {
<<<<<<< HEAD
ctx := context.Background()
c, err := bigcache.New(ctx, bigcache.DefaultConfig(time.Hour*24))
if err != nil {
logger.Fatalln("can't initialize caching")
}
cache = c
r := mux.NewRouter()
r.Use(securityHeaders)
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)
r.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
render("error", w, map[string]string{
"Status": "404",
"Error": "page not found",
})
})
=======
logger := utils.NewLogger(os.Stdout)
>>>>>>> upstream/main
server := &http.Server{
Handler: handlers.New(logger, staticFiles),

View File

@ -1,77 +0,0 @@
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"strings"
"github.com/gorilla/mux"
)
func isValidExt(ext string) bool {
valid := []string{"jpg", "jpeg", "png", "gif"}
for _, c := range valid {
if strings.ToLower(ext) == c {
return true
}
}
return false
}
func extractURL(image string) string {
u, err := url.Parse(image)
if err != nil {
return ""
}
return fmt.Sprintf("/images%s", u.Path)
}
func proxyHandler(w http.ResponseWriter, r *http.Request) {
v := mux.Vars(r)
f := v["filename"]
ext := v["ext"]
if !isValidExt(ext) {
w.WriteHeader(http.StatusBadRequest)
render("error", w, map[string]string{
"Status": "400",
"Error": "Something went wrong",
})
return
}
// first segment of URL resize the image to reduce bandwith usage.
url := fmt.Sprintf("https://t2.genius.com/unsafe/300x300/https://images.genius.com/%s.%s", f, ext)
res, 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
}
if res.StatusCode != http.StatusOK {
w.WriteHeader(http.StatusInternalServerError)
render("error", w, map[string]string{
"Status": "500",
"Error": "something went wrong",
})
return
}
w.Header().Add("Content-type", fmt.Sprintf("image/%s", ext))
_, err = io.Copy(w, res.Body)
if err != nil {
logger.Errorln(err)
}
}

View File

@ -1,64 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
)
type response struct {
Response struct {
Sections sections
}
}
type result struct {
ArtistNames string `json:"artist_names"`
Title string
Path string
Thumbnail string `json:"song_art_image_thumbnail_url"`
}
type hits []struct {
Result result
}
type sections []struct {
Type string
Hits hits
}
type renderVars struct {
Query string
Sections sections
}
func searchHandler(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query().Get("q")
url := fmt.Sprintf(`https://genius.com/api/search/multi?q=%s`, url.QueryEscape(query))
res, 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",
})
}
defer res.Body.Close()
var data response
d := json.NewDecoder(res.Body)
err = d.Decode(&data)
if err != nil {
logger.Errorln(err)
}
vars := renderVars{query, data.Response.Sections}
render("search", w, vars)
}