early database work

Signed-off-by: ari melody <ari@arimelody.me>
This commit is contained in:
ari melody 2024-03-18 20:19:04 +00:00
parent 6efd47c6c6
commit 5eecef1d78
8 changed files with 512 additions and 96 deletions

View file

@ -1,19 +1,47 @@
package music package music
import (
"fmt"
"time"
)
var ari = Artist{
Name: "ari melody",
Website: "https://arimelody.me",
}
var zaire = Artist{
Name: "zaire",
Website: "https://supitszaire.com",
}
var mae = Artist{
Name: "mae taylor",
Website: "https://mae.wtf",
}
var loudar = Artist{
Name: "Loudar",
Website: "https://alex.targoninc.com",
}
var red = Artist{
Name: "smoljorb",
}
func make_date_work(date string) time.Time {
res, err := time.Parse("2-Jan-2006", date)
if err != nil {
fmt.Printf("somehow we failed to parse %s! falling back to epoch :]\n", date)
return time.Unix(0, 0)
}
return res
}
var placeholders = []Album{ var placeholders = []Album{
{ {
Id: "test", Id: "test",
Title: "test album", Title: "test album",
Type: "album", Type: "album",
Year: 2024, ReleaseDate: make_date_work("18-Mar-2024"),
Buyname: "go get it!!", Buyname: "go get it!!",
Buylink: "https://arimelody.me/", Buylink: "https://arimelody.me/",
Artists: []string{
"ari melody",
"zaire",
"mae taylor",
"Loudar",
},
Links: []AlbumLink{ Links: []AlbumLink{
AlbumLink{ AlbumLink{
Name: "youtube", Name: "youtube",
@ -23,23 +51,19 @@ var placeholders = []Album{
Description: "she sample on my text 'til i 🚫🚫🚫", Description: "she sample on my text 'til i 🚫🚫🚫",
Credits: []AlbumCredit{ Credits: []AlbumCredit{
AlbumCredit{ AlbumCredit{
Name: "ari melody", Artist: &ari,
Url: "https://arimelody.me",
Role: "having the swag", Role: "having the swag",
}, },
AlbumCredit{ AlbumCredit{
Name: "zaire", Artist: &zaire,
Url: "https://supitszaire.com",
Role: "having the swag", Role: "having the swag",
}, },
AlbumCredit{ AlbumCredit{
Name: "mae taylor", Artist: &mae,
Url: "https://mae.wtf",
Role: "having the swag", Role: "having the swag",
}, },
AlbumCredit{ AlbumCredit{
Name: "Loudar", Artist: &loudar,
Url: "https://alex.targoninc.com",
Role: "having the swag", Role: "having the swag",
}, },
}, },
@ -122,21 +146,32 @@ var placeholders = []Album{
Id: "free2play", Id: "free2play",
Title: "free2play", Title: "free2play",
Type: "upcoming", Type: "upcoming",
Year: 2024, ReleaseDate: make_date_work("17-Mar-2024"),
Buyname: "pre-order", Buyname: "pre-order",
Buylink: "https://arimelody.me/", Buylink: "https://arimelody.me/",
Artists: []string{ "ari melody" },
Description: "hello from your local SPACEGIRL! 💫", Description: "hello from your local SPACEGIRL! 💫",
Credits: []AlbumCredit{
AlbumCredit{
Artist: &ari,
Role: "vocals",
},
AlbumCredit{
Artist: &ari,
Role: "production",
},
AlbumCredit{
Artist: &ari,
Role: "artwork",
},
},
}, },
{ {
Id: "dream", Id: "dream",
Title: "Dream", Title: "Dream",
Type: "single", Type: "single",
Year: 2022, ReleaseDate: make_date_work("11-Nov-2024"),
Artists: []string{ "mellodoot" },
Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Dream.webp", Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Dream.webp",
Buylink: "https://arimelody.bandcamp.com/track/dream", Buylink: "https://arimelody.bandcamp.com/track/dream",
Free: false,
Links: []AlbumLink{ Links: []AlbumLink{
AlbumLink{ AlbumLink{
Name: "spotify", Name: "spotify",
@ -158,18 +193,15 @@ var placeholders = []Album{
Description: "living the dream 🌌 ✨", Description: "living the dream 🌌 ✨",
Credits: []AlbumCredit{ Credits: []AlbumCredit{
AlbumCredit{ AlbumCredit{
Name: "ari melody", Artist: &ari,
Url: "https://arimelody.me",
Role: "vocals", Role: "vocals",
}, },
AlbumCredit{ AlbumCredit{
Name: "ari melody", Artist: &ari,
Url: "https://arimelody.me",
Role: "production", Role: "production",
}, },
AlbumCredit{ AlbumCredit{
Name: "ari melody", Artist: &ari,
Url: "https://arimelody.me",
Role: "artwork", Role: "artwork",
}, },
}, },
@ -205,8 +237,7 @@ var placeholders = []Album{
Id: "gomyway", Id: "gomyway",
Title: "Go My Way", Title: "Go My Way",
Type: "single", Type: "single",
Year: 2021, ReleaseDate: make_date_work("24-Jan-2021"),
Artists: []string{ "mellodoot" },
Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Go_My_Way.webp", Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Go_My_Way.webp",
Buylink: "https://arimelody.bandcamp.com/track/go-my-way", Buylink: "https://arimelody.bandcamp.com/track/go-my-way",
Links: []AlbumLink{ Links: []AlbumLink{
@ -228,13 +259,26 @@ var placeholders = []Album{
}, },
}, },
Description: "hey! go my way! 💥 ✨", Description: "hey! go my way! 💥 ✨",
Credits: []AlbumCredit{
AlbumCredit{
Artist: &ari,
Role: "vocals",
},
AlbumCredit{
Artist: &ari,
Role: "production",
},
AlbumCredit{
Artist: &ari,
Role: "artwork",
},
},
}, },
{ {
Id: "rowboat", Id: "rowboat",
Title: "Rowboat", Title: "Rowboat",
Type: "single", Type: "single",
Year: 2020, ReleaseDate: make_date_work("12-Mar-2020"),
Artists: []string{ "mellodoot" },
Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Rowboat.webp", Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Rowboat.webp",
Buylink: "https://arimelody.bandcamp.com/track/rowboat", Buylink: "https://arimelody.bandcamp.com/track/rowboat",
Links: []AlbumLink{ Links: []AlbumLink{
@ -256,13 +300,26 @@ var placeholders = []Album{
}, },
}, },
Description: "let's take a trip. i've got a goddamn boat ⛵️", Description: "let's take a trip. i've got a goddamn boat ⛵️",
Credits: []AlbumCredit{
AlbumCredit{
Artist: &ari,
Role: "vocals",
},
AlbumCredit{
Artist: &ari,
Role: "production",
},
AlbumCredit{
Artist: &ari,
Role: "artwork",
},
},
}, },
{ {
Id: "helloworld", Id: "helloworld",
Title: "Hello World", Title: "Hello World",
Type: "single", Type: "single",
Year: 2019, ReleaseDate: make_date_work("25-Dec-2019"),
Artists: []string{ "mellodoot" },
Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Hello_World.webp", Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Hello_World.webp",
Buylink: "https://arimelody.bandcamp.com/track/hello-world", Buylink: "https://arimelody.bandcamp.com/track/hello-world",
Links: []AlbumLink{ Links: []AlbumLink{
@ -284,13 +341,26 @@ var placeholders = []Album{
}, },
}, },
Description: "we'll dawn a new frontier! 👾", Description: "we'll dawn a new frontier! 👾",
Credits: []AlbumCredit{
AlbumCredit{
Artist: &ari,
Role: "vocals",
},
AlbumCredit{
Artist: &ari,
Role: "production",
},
AlbumCredit{
Artist: &ari,
Role: "artwork",
},
},
}, },
{ {
Id: "sine", Id: "sine",
Title: "Sine", Title: "Sine",
Type: "single", Type: "single",
Artists: []string{ "zaire", "mellodoot" }, ReleaseDate: make_date_work("07-Dec-2019"),
Year: 2019,
Artwork: "https://mellodoot.com/img/music_artwork/zaire_-_Sine_ft._mellodoot.webp", Artwork: "https://mellodoot.com/img/music_artwork/zaire_-_Sine_ft._mellodoot.webp",
Links: []AlbumLink{ Links: []AlbumLink{
{ {
@ -306,13 +376,22 @@ var placeholders = []Album{
Url: "https://www.youtube.com/watch?v=z1H1s6VRnyY", Url: "https://www.youtube.com/watch?v=z1H1s6VRnyY",
}, },
}, },
Credits: []AlbumCredit{
AlbumCredit{
Artist: &zaire,
Role: "production",
},
AlbumCredit{
Artist: &ari,
Role: "production",
},
},
}, },
{ {
Id: "10", Id: "10",
Title: "10", Title: "10",
Type: "single", Type: "single",
Year: 2019, ReleaseDate: make_date_work("29-Sep-2019"),
Artists: []string{ "mellodoot" },
Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_10.webp", Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_10.webp",
Buylink: "https://arimelody.bandcamp.com/track/10", Buylink: "https://arimelody.bandcamp.com/track/10",
Links: []AlbumLink{ Links: []AlbumLink{
@ -333,13 +412,22 @@ var placeholders = []Album{
Url: "https://www.youtube.com/watch?v=C7WP5L2BK4U", Url: "https://www.youtube.com/watch?v=C7WP5L2BK4U",
}, },
}, },
Credits: []AlbumCredit{
AlbumCredit{
Artist: &ari,
Role: "production",
},
AlbumCredit{
Artist: &ari,
Role: "artwork",
},
},
}, },
{ {
Id: "mad", Id: "mad",
Title: "MAD", Title: "MAD",
Type: "single", Type: "single",
Year: 2018, ReleaseDate: make_date_work("03-Nov-2018"),
Artists: []string{ "mellodoot" },
Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_MAD.webp", Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_MAD.webp",
Buylink: "https://arimelody.bandcamp.com/track/mad", Buylink: "https://arimelody.bandcamp.com/track/mad",
Links: []AlbumLink{ Links: []AlbumLink{
@ -360,13 +448,26 @@ var placeholders = []Album{
Url: "https://www.youtube.com/watch?v=OB-Pk6p6mfQ", Url: "https://www.youtube.com/watch?v=OB-Pk6p6mfQ",
}, },
}, },
Credits: []AlbumCredit{
AlbumCredit{
Artist: &ari,
Role: "vocals",
},
AlbumCredit{
Artist: &ari,
Role: "production",
},
AlbumCredit{
Artist: &ari,
Role: "artwork",
},
},
}, },
{ {
Id: "welcomingparty", Id: "welcomingparty",
Title: "Welcoming Party", Title: "Welcoming Party",
Type: "album", Type: "album",
Year: 2018, ReleaseDate: make_date_work("01-Nov-2018"),
Artists: []string{ "mellodoot" },
Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Welcoming_Party.webp", Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Welcoming_Party.webp",
Buylink: "https://arimelody.bandcamp.com/album/welcoming-party", Buylink: "https://arimelody.bandcamp.com/album/welcoming-party",
Links: []AlbumLink{ Links: []AlbumLink{
@ -387,6 +488,16 @@ var placeholders = []Album{
Url: "https://www.youtube.com/playlist?list=PLBG_QJeOHrB5EeniiXBIlHpoQbD6CUJca", Url: "https://www.youtube.com/playlist?list=PLBG_QJeOHrB5EeniiXBIlHpoQbD6CUJca",
}, },
}, },
Credits: []AlbumCredit{
AlbumCredit{
Artist: &ari,
Role: "production",
},
AlbumCredit{
Artist: &ari,
Role: "artwork",
},
},
// "tracks": [ // "tracks": [
// { // {
// "title": "Paradigm" // "title": "Paradigm"
@ -409,11 +520,10 @@ var placeholders = []Album{
Id: "howtheyknow2018", Id: "howtheyknow2018",
Title: "How They Know (2018)", Title: "How They Know (2018)",
Type: "single", Type: "single",
Year: 2018, ReleaseDate: make_date_work("27-Feb-2018"),
Artists: []string{ "mellodoot" },
Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_How_They_Know_2018.webp", Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_How_They_Know_2018.webp",
Buyname: "free download",
Buylink: "https://arimelody.bandcamp.com/track/how-they-know-2018-remastered", Buylink: "https://arimelody.bandcamp.com/track/how-they-know-2018-remastered",
Free: true,
Links: []AlbumLink{ Links: []AlbumLink{
{ {
Name: "soundcloud", Name: "soundcloud",
@ -424,16 +534,25 @@ var placeholders = []Album{
Url: "https://www.youtube.com/watch?v=mbAgSwCzyMw", Url: "https://www.youtube.com/watch?v=mbAgSwCzyMw",
}, },
}, },
Credits: []AlbumCredit{
AlbumCredit{
Artist: &ari,
Role: "production",
},
AlbumCredit{
Artist: &red,
Role: "artwork",
},
},
}, },
{ {
Id: "howtheyknow", Id: "howtheyknow",
Title: "How They Know", Title: "How They Know",
Type: "single", Type: "single",
Year: 2017, ReleaseDate: make_date_work("29-Nov-2017"),
Artists: []string{ "mellodoot" },
Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_How_They_Know.webp", Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_How_They_Know.webp",
Buyname: "free download",
Buylink: "https://arimelody.bandcamp.com/track/how-they-know", Buylink: "https://arimelody.bandcamp.com/track/how-they-know",
Free: true,
Links: []AlbumLink{ Links: []AlbumLink{
{ {
Name: "soundcloud", Name: "soundcloud",
@ -444,16 +563,25 @@ var placeholders = []Album{
Url: "https://www.youtube.com/watch?v=q6lzKuG1Emo", Url: "https://www.youtube.com/watch?v=q6lzKuG1Emo",
}, },
}, },
Credits: []AlbumCredit{
AlbumCredit{
Artist: &ari,
Role: "production",
},
AlbumCredit{
Artist: &red,
Role: "artwork",
},
},
}, },
{ {
Id: "traveller", Id: "traveller",
Title: "Traveller", Title: "Traveller",
Type: "single", Type: "single",
Year: 2017, ReleaseDate: make_date_work("24-Sep-2017"),
Artists: []string{ "mellodoot" },
Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Traveller.webp", Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Traveller.webp",
Buyname: "free download",
Buylink: "https://arimelody.bandcamp.com/track/traveller", Buylink: "https://arimelody.bandcamp.com/track/traveller",
Free: true,
Links: []AlbumLink{ Links: []AlbumLink{
{ {
Name: "soundcloud", Name: "soundcloud",
@ -465,6 +593,16 @@ var placeholders = []Album{
}, },
}, },
Description: "an 8-bit expedition! ⚔️🛡️", Description: "an 8-bit expedition! ⚔️🛡️",
Credits: []AlbumCredit{
AlbumCredit{
Artist: &ari,
Role: "production",
},
AlbumCredit{
Artist: &ari,
Role: "artwork",
},
},
}, },
} }
@ -477,7 +615,11 @@ func GetAlbum(id string) (Album, bool) {
return Album{}, false return Album{}, false
} }
func QueryAll() ([]Album) { func QueryAllAlbums() ([]Album) {
return placeholders return placeholders
} }
func QueryAllArtists() ([]Artist) {
return []Artist{ ari, zaire, mae, loudar, red }
}

View file

@ -3,19 +3,23 @@ package music
import ( import (
"regexp" "regexp"
"strings" "strings"
"time"
) )
type ( type (
Artist struct {
Name string;
Website string;
}
Album struct { Album struct {
Id string; Id string;
Title string; Title string;
Type string; Type string;
Year int; ReleaseDate time.Time;
Artists []string;
Artwork string; Artwork string;
Buyname string; Buyname string;
Buylink string; Buylink string;
Free bool;
Links []AlbumLink; Links []AlbumLink;
Description string; Description string;
Credits []AlbumCredit; Credits []AlbumCredit;
@ -28,26 +32,59 @@ type (
} }
AlbumCredit struct { AlbumCredit struct {
Artist *Artist;
Role string; Role string;
Name string;
Url string;
} }
) )
func (album Album) PrintArtists() string { func (album Album) GetUniqueArtists() []Artist {
if len(album.Artists) == 1 { if len(album.Credits) == 1 {
return album.Artists[0] return []Artist{ *album.Credits[0].Artist }
} }
res := strings.Join(album.Artists[:len(album.Artists) - 1], ", ")
res += " & " + album.Artists[len(album.Artists) - 1] // create a map of artists to prevent duplicates
m := map[string]Artist{}
for _, credit := range album.Credits {
artist := *credit.Artist
m[artist.Name] = artist
}
// now create the actual array to send
res := []Artist{}
for _, artist := range m {
res = append(res, artist)
}
return res
}
func (album Album) PrintArtists() string {
if len(album.Credits) == 1 {
return album.Credits[0].Artist.Name
}
artists := album.GetUniqueArtists()
names := []string{}
for _, artist := range artists {
names = append(names, artist.Name)
}
res := strings.Join(names[:len(names) - 1], ", ")
res += " & " + names[len(names) - 1]
return res return res
} }
func (album Album) PrintCommaArtists() string { func (album Album) PrintCommaArtists() string {
if len(album.Artists) == 1 { if len(album.Credits) == 1 {
return album.Artists[0] return album.Credits[0].Artist.Name
} }
return strings.Join(album.Artists[:len(album.Artists)], ", ")
artists := album.GetUniqueArtists()
names := []string{}
for _, artist := range artists {
names = append(names, artist.Name)
}
return strings.Join(names[:len(names)], ", ")
} }
func (album Album) ResolveArtwork() string { func (album Album) ResolveArtwork() string {
@ -57,7 +94,15 @@ func (album Album) ResolveArtwork() string {
return "/img/music-artwork/default.png" return "/img/music-artwork/default.png"
} }
func (album Album) GetReleaseYear() int {
return album.ReleaseDate.Year()
}
func (link AlbumLink) NormaliseName() string { func (link AlbumLink) NormaliseName() string {
re := regexp.MustCompile(`[^a-z0-9]`) re := regexp.MustCompile(`[^a-z0-9]`)
return strings.ToLower(re.ReplaceAllString(link.Name, "")) return strings.ToLower(re.ReplaceAllString(link.Name, ""))
} }
func (credit AlbumCredit) ResolveArtist() Artist {
return *credit.Artist
}

118
db.go Normal file
View file

@ -0,0 +1,118 @@
package main
import (
"arimelody.me/arimelody.me/api/v1/music"
"fmt"
"os"
"time"
_ "github.com/lib/pq"
"github.com/jmoiron/sqlx"
)
var schema =
`CREATE TABLE IF NOT EXISTS Artists (
id SERIAL primary key,
name text,
website text
);
CREATE TABLE IF NOT EXISTS Albums (
id varchar(64) primary key,
title text not null,
release_date date not null,
artwork text,
buyname text,
buylink text,
description text,
lyrics text
);
CREATE TABLE IF NOT EXISTS AlbumLinks (
id SERIAL primary key,
album varchar(64) references Albums(id),
name text,
url text
);
CREATE TABLE IF NOT EXISTS AlbumCredits (
id SERIAL primary key,
album varchar(64) references Albums(id),
artist int references Artists(id),
role text
);`
func PushArtist(db *sqlx.DB, artist music.Artist) {
query := "SELECT count(*) FROM Artists WHERE name=$1"
var count int
err := db.Get(&count, query, artist.Name)
if err != nil {
fmt.Printf("error while pushing artist [%s] to the database: %v\n", artist.Name, err)
}
query = "INSERT INTO artists (name, website) VALUES ($1, $2)"
if count != 0 {
query = "UPDATE artists SET website=$2 WHERE name=$1"
}
fmt.Printf("saving artist [%s] to the database...", artist.Name)
_, err = db.Exec(query,
&artist.Name,
&artist.Website,
)
if err != nil {
fmt.Printf("error while pushing artist [%s] to the database: %v\n", artist.Name, err)
}
fmt.Printf("done!\n")
// defer db.Close()
}
func PushAlbum(db *sqlx.DB, album music.Album) {
query := "SELECT count(*) FROM Albums WHERE id=$1"
var count int
err := db.Get(&count, query, album.Id)
if err != nil {
fmt.Printf("error while pushing album [%s] to the database: %v\n", album.Id, err)
}
query = "INSERT INTO Albums (id, title, release_date, artwork, buyname, buylink, description, lyrics) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)"
if count != 0 {
query = "UPDATE Albums SET title=$2, release_date=$3, artwork=$4, buyname=$5, buylink=$6, description=$7, lyrics=$8 WHERE id=$1"
}
fmt.Printf("saving album [%s] to the database...", album.Id)
_, err = db.Exec(query,
&album.Id,
&album.Title,
album.ReleaseDate.Format("2-Jan-2006"),
&album.Artwork,
&album.Buyname,
&album.Buylink,
&album.Description,
&album.Lyrics,
)
if err != nil {
fmt.Printf("error while pushing album [%s] to the database: %v\n", album.Id, err)
}
fmt.Printf("done!\n")
// defer db.Close()
}
func InitDatabase() *sqlx.DB {
db, err := sqlx.Connect("postgres", "user=arimimi dbname=arimelody password=fuckingpassword sslmode=disable")
if err != nil {
fmt.Fprintf(os.Stderr, "unable to create database connection pool: %v\n", err)
os.Exit(1)
}
db.SetConnMaxLifetime(time.Minute * 3)
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(10)
db.MustExec(schema)
return db
}

13
go.mod
View file

@ -2,4 +2,15 @@ module arimelody.me/arimelody.me
go 1.22.1 go 1.22.1
require github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 // indirect require (
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.5.5 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/lib/pq v1.10.9 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/text v0.14.0 // indirect
)

28
go.sum
View file

@ -1,2 +1,30 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 h1:k4Tw0nt6lwro3Uin8eqoET7MDA4JnT8YgbCjc/g5E3k= github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 h1:k4Tw0nt6lwro3Uin8eqoET7MDA4JnT8YgbCjc/g5E3k=
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

104
main.go
View file

@ -10,6 +10,7 @@ import (
"log" "log"
"strings" "strings"
"time" "time"
"strconv"
"github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html" "github.com/gomarkdown/markdown/html"
@ -35,49 +36,69 @@ var templates = template.Must(template.ParseFiles(
"views/music-gateway.html", "views/music-gateway.html",
)) ))
func web_handler(writer http.ResponseWriter, req *http.Request) { func log_request(req *http.Request, code int, start_time time.Time) {
uri := req.URL.Path now := time.Now()
difference := (now.Nanosecond() - start_time.Nanosecond()) / 1_000_000
elapsed := "<1"
if difference >= 1 {
elapsed = strconv.Itoa(difference)
}
fmt.Printf("[%s] %s %s (%s)\n", fmt.Printf("[%s] %s %s - %d (%sms) (%s)\n",
time.Now().Format(time.UnixDate), now.Format(time.UnixDate),
req.Method, req.Method,
req.URL.Path, req.URL.Path,
code,
elapsed,
req.Header["User-Agent"][0], req.Header["User-Agent"][0],
) )
}
func web_handler(writer http.ResponseWriter, req *http.Request) {
uri := req.URL.Path
start_time := time.Now()
if req.URL.Path == "/" { if req.URL.Path == "/" {
index_handler(writer, req) code := index_handler(writer, req)
log_request(req, code, start_time)
return return
} }
if uri == "/music" { if uri == "/music" {
music_handler(writer, req) code := music_handler(writer, req)
log_request(req, code, start_time)
return return
} }
if strings.HasPrefix(uri, "/music/") { if strings.HasPrefix(uri, "/music/") {
music_gateway_handler(writer, req) code := music_gateway_handler(writer, req)
log_request(req, code, start_time)
return return
} }
static_handler(writer, req) code := static_handler(writer, req)
log_request(req, code, start_time)
} }
func index_handler(writer http.ResponseWriter, req *http.Request) { func index_handler(writer http.ResponseWriter, req *http.Request) int {
err := templates.ExecuteTemplate(writer, "index.html", nil) err := templates.ExecuteTemplate(writer, "index.html", nil)
if err != nil { if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
return 500
} }
return 200
} }
func music_handler(writer http.ResponseWriter, req *http.Request) { func music_handler(writer http.ResponseWriter, req *http.Request) int {
err := templates.ExecuteTemplate(writer, "music.html", music.QueryAll()) err := templates.ExecuteTemplate(writer, "music.html", music.QueryAllAlbums())
if err != nil { if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
return 500
} }
return 200
} }
func music_gateway_handler(writer http.ResponseWriter, req *http.Request) { func music_gateway_handler(writer http.ResponseWriter, req *http.Request) int {
if len(req.URL.Path) <= len("/music/") { if len(req.URL.Path) <= len("/music/") {
http.Error(writer, "400 bad request", http.StatusBadRequest) http.Error(writer, "400 bad request", http.StatusBadRequest)
return return 400
} }
id := req.URL.Path[len("/music/"):] id := req.URL.Path[len("/music/"):]
@ -86,28 +107,61 @@ func music_gateway_handler(writer http.ResponseWriter, req *http.Request) {
album, ok := music.GetAlbum(id) album, ok := music.GetAlbum(id)
if !ok { if !ok {
http.Error(writer, "404 not found", http.StatusNotFound) http.Error(writer, "404 not found", http.StatusNotFound)
return return 404
} }
err := templates.ExecuteTemplate(writer, "music-gateway.html", album) err := templates.ExecuteTemplate(writer, "music-gateway.html", album)
if err != nil { if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
return 500
} }
return 200
} }
func static_handler(writer http.ResponseWriter, req *http.Request) { func static_handler(writer http.ResponseWriter, req *http.Request) int {
filename := "public/" + req.URL.Path[1:] filename := "public/" + req.URL.Path[1:]
body, err := os.ReadFile(filename)
// check the file's metadata
info, err := os.Stat(filename)
if err != nil { if err != nil {
http.Error(writer, "404 not found", http.StatusNotFound) http.Error(writer, "404 not found", http.StatusNotFound)
return return 404
} }
if len(req.Header["If-Modified-Since"]) > 0 && req.Header["If-Modified-Since"][0] != "" {
if_modified_since_time, err := time.Parse(http.TimeFormat, req.Header["If-Modified-Since"][0])
if err != nil {
http.Error(writer, "400 bad request", http.StatusBadRequest)
return 400
}
if req.Header["If-Modified-Since"][0] == info.ModTime().Format(http.TimeFormat) || if_modified_since_time.After(info.ModTime()) {
writer.WriteHeader(304) // not modified
return 304
}
}
// set other nice headers
writer.Header().Set("Cache-Control", "max-age=86400")
writer.Header().Set("Last-Modified", info.ModTime().Format(http.TimeFormat))
// Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
// Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
// read the file
body, err := os.ReadFile(filename)
if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
return 500
}
// setting MIME types
filetype := filename[strings.LastIndex(filename, ".") + 1:] filetype := filename[strings.LastIndex(filename, ".") + 1:]
if mime_type, ok := mime_types[filetype]; ok { if mime_type, ok := mime_types[filetype]; ok {
writer.Header().Set("Content-Type", mime_type) writer.Header().Set("Content-Type", mime_type)
} else { } else {
writer.Header().Set("Content-Type", "text/plain; charset=utf-8") writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
} }
writer.Write([]byte(body)) writer.Write([]byte(body))
return 200
} }
func parse_markdown(md []byte) []byte { func parse_markdown(md []byte) []byte {
@ -122,7 +176,23 @@ func parse_markdown(md []byte) []byte {
return markdown.Render(doc, renderer) return markdown.Render(doc, renderer)
} }
func push_to_db_this_is_a_testing_thing_and_will_be_superfluous_later() {
db := InitDatabase()
for _, album := range music.QueryAllAlbums() {
PushAlbum(db, album)
}
for _, artist := range music.QueryAllArtists() {
PushArtist(db, artist)
}
defer db.Close()
}
func main() { func main() {
// push_to_db_this_is_a_testing_thing_and_will_be_superfluous_later()
http.HandleFunc("/", web_handler) http.HandleFunc("/", web_handler)
log.Fatal(http.ListenAndServe(":8080", nil)) log.Fatal(http.ListenAndServe(":8080", nil))
} }

View file

@ -216,7 +216,7 @@ div#info {
#title-container { #title-container {
display: flex; display: flex;
align-items: baseline; align-items: last baseline;
flex-direction: row; flex-direction: row;
} }

View file

@ -11,7 +11,7 @@
<meta name="description" content="Stream &quot;{{.Title}}&quot; by {{.PrintArtists}} on all platforms!"> <meta name="description" content="Stream &quot;{{.Title}}&quot; by {{.PrintArtists}} on all platforms!">
<meta name="author" content="{{.PrintArtists}}"> <meta name="author" content="{{.PrintArtists}}">
<meta name="keywords" content="{{.PrintCommaArtists}}, music, {{.Title}}, {{.Id}}, {{.Year}}"> <meta name="keywords" content="{{.PrintCommaArtists}}, music, {{.Title}}, {{.Id}}, {{.GetReleaseYear}}">
<meta property="og:url" content="https://arimelody.me/music/{{.Id}}"> <meta property="og:url" content="https://arimelody.me/music/{{.Id}}">
<meta property="og:type" content="website"> <meta property="og:type" content="website">
@ -61,23 +61,26 @@
<div id="main"> <div id="main">
<div id="title-container"> <div id="title-container">
<h1 id="title">{{.Title}}</h1> <h1 id="title">{{.Title}}</h1>
<span id="year">{{.Year}}</span> <span id="year">{{.GetReleaseYear}}</span>
</div> </div>
<p id="artist">{{.PrintArtists}}</p> <p id="artist">{{.PrintArtists}}</p>
<p id="type" class="{{.Type}}">{{.Type}}</p> <p id="type" class="{{.Type}}">{{.Type}}</p>
{{if .Description}}
<p id="description">
{{.Description}}
</p>
{{end}}
<div id="links"> <div id="links">
{{if .Buylink}} {{if .Buylink}}
<a href="{{.Buylink}}" target="_blank" class="buy"> <a href="{{.Buylink}}" target="_blank" class="buy">
{{if .Buyname}} {{if .Buyname}}
{{.Buyname}} {{.Buyname}}
{{else}} {{else}}
{{if .Free}} buy
download
{{else}}
buy
{{end}} {{end}}
{{end}}</a> </a>
{{end}} {{end}}
{{range .Links}} {{range .Links}}
@ -87,10 +90,7 @@
{{end}} {{end}}
</div> </div>
<p id="description"> {{if or .Credits .Lyrics}}
{{.Description}}
</p>
<ul id="extras"> <ul id="extras">
{{if .Credits}} {{if .Credits}}
<li><a href="#credits">credits</a></li> <li><a href="#credits">credits</a></li>
@ -100,6 +100,7 @@
<li><a href="#lyrics">lyrics</a></li> <li><a href="#lyrics">lyrics</a></li>
{{end}} {{end}}
</ul> </ul>
{{end}}
<p id="share">share</p> <p id="share">share</p>
</div> </div>
@ -110,7 +111,8 @@
<h2>credits:</h2> <h2>credits:</h2>
<ul> <ul>
{{range .Credits}} {{range .Credits}}
<li><strong><a href="{{.Url}}">{{.Name}}</a></strong>: {{.Role}}</li> {{$Artist := .ResolveArtist}}
<li><strong><a href="{{$Artist.Website}}">{{$Artist.Name}}</a></strong>: {{.Role}}</li>
{{end}} {{end}}
</ul> </ul>
</div> </div>