feat: replace text/template with templ and refactor code
This commit is contained in:
74
data/album.go
Normal file
74
data/album.go
Normal file
@ -0,0 +1,74 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
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
|
||||
json.Unmarshal([]byte(pageMetadata), &albumMetadataFromPage)
|
||||
|
||||
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)
|
||||
a.About[1] = ""
|
||||
|
||||
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)
|
||||
}
|
||||
|
15
data/annotation.go
Normal file
15
data/annotation.go
Normal file
@ -0,0 +1,15 @@
|
||||
package data
|
||||
|
||||
type AnnotationsResponse struct {
|
||||
Response struct {
|
||||
Referent struct {
|
||||
Annotations []Annotation `json:"annotations"`
|
||||
} `json:"referent"`
|
||||
} `json:"response"`
|
||||
}
|
||||
|
||||
type Annotation struct {
|
||||
Body struct {
|
||||
Html string `json:"html"`
|
||||
} `json:"body"`
|
||||
}
|
112
data/lyrics.go
Normal file
112
data/lyrics.go
Normal file
@ -0,0 +1,112 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
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 {
|
||||
logrus.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 {
|
||||
logrus.Errorln(err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
var data songResponse
|
||||
decoder := json.NewDecoder(res.Body)
|
||||
err = decoder.Decode(&data)
|
||||
if err != nil {
|
||||
logrus.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)
|
||||
}
|
16
data/proxy.go
Normal file
16
data/proxy.go
Normal file
@ -0,0 +1,16 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func ExtractImageURL(image string) string {
|
||||
u, err := url.Parse(image)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("/images%s", u.Path)
|
||||
}
|
||||
|
28
data/search.go
Normal file
28
data/search.go
Normal file
@ -0,0 +1,28 @@
|
||||
package data
|
||||
|
||||
type SearchResponse 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 SearchResults struct {
|
||||
Query string
|
||||
Sections sections
|
||||
}
|
46
data/utils.go
Normal file
46
data/utils.go
Normal file
@ -0,0 +1,46 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/caffix/cloudflare-roundtripper/cfrt"
|
||||
)
|
||||
|
||||
const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36"
|
||||
|
||||
var client = &http.Client{
|
||||
Timeout: 20 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 15 * time.Second,
|
||||
KeepAlive: 15 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
},
|
||||
}
|
||||
|
||||
func sendRequest(u string) (*http.Response, error) {
|
||||
url, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.Transport, err = cfrt.New(client.Transport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := &http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: url,
|
||||
Header: map[string][]string{
|
||||
"Accept-Language": {"en-US"},
|
||||
"User-Agent": {UA},
|
||||
},
|
||||
}
|
||||
|
||||
return client.Do(req)
|
||||
}
|
Reference in New Issue
Block a user