diff --git a/api/v1/music/music.go b/api/v1/music/music.go index c6454d7..190678f 100644 --- a/api/v1/music/music.go +++ b/api/v1/music/music.go @@ -1,19 +1,47 @@ 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{ { Id: "test", Title: "test album", Type: "album", - Year: 2024, + ReleaseDate: make_date_work("18-Mar-2024"), Buyname: "go get it!!", Buylink: "https://arimelody.me/", - Artists: []string{ - "ari melody", - "zaire", - "mae taylor", - "Loudar", - }, Links: []AlbumLink{ AlbumLink{ Name: "youtube", @@ -23,23 +51,19 @@ var placeholders = []Album{ Description: "she sample on my text 'til i 🚫🚫🚫", Credits: []AlbumCredit{ AlbumCredit{ - Name: "ari melody", - Url: "https://arimelody.me", + Artist: &ari, Role: "having the swag", }, AlbumCredit{ - Name: "zaire", - Url: "https://supitszaire.com", + Artist: &zaire, Role: "having the swag", }, AlbumCredit{ - Name: "mae taylor", - Url: "https://mae.wtf", + Artist: &mae, Role: "having the swag", }, AlbumCredit{ - Name: "Loudar", - Url: "https://alex.targoninc.com", + Artist: &loudar, Role: "having the swag", }, }, @@ -122,21 +146,32 @@ var placeholders = []Album{ Id: "free2play", Title: "free2play", Type: "upcoming", - Year: 2024, + ReleaseDate: make_date_work("17-Mar-2024"), Buyname: "pre-order", Buylink: "https://arimelody.me/", - Artists: []string{ "ari melody" }, 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", Title: "Dream", Type: "single", - Year: 2022, - Artists: []string{ "mellodoot" }, + ReleaseDate: make_date_work("11-Nov-2024"), Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Dream.webp", Buylink: "https://arimelody.bandcamp.com/track/dream", - Free: false, Links: []AlbumLink{ AlbumLink{ Name: "spotify", @@ -158,18 +193,15 @@ var placeholders = []Album{ Description: "living the dream 🌌 ✨", Credits: []AlbumCredit{ AlbumCredit{ - Name: "ari melody", - Url: "https://arimelody.me", + Artist: &ari, Role: "vocals", }, AlbumCredit{ - Name: "ari melody", - Url: "https://arimelody.me", + Artist: &ari, Role: "production", }, AlbumCredit{ - Name: "ari melody", - Url: "https://arimelody.me", + Artist: &ari, Role: "artwork", }, }, @@ -205,8 +237,7 @@ var placeholders = []Album{ Id: "gomyway", Title: "Go My Way", Type: "single", - Year: 2021, - Artists: []string{ "mellodoot" }, + ReleaseDate: make_date_work("24-Jan-2021"), Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Go_My_Way.webp", Buylink: "https://arimelody.bandcamp.com/track/go-my-way", Links: []AlbumLink{ @@ -228,13 +259,26 @@ var placeholders = []Album{ }, }, Description: "hey! go my way! 💥 ✨", + Credits: []AlbumCredit{ + AlbumCredit{ + Artist: &ari, + Role: "vocals", + }, + AlbumCredit{ + Artist: &ari, + Role: "production", + }, + AlbumCredit{ + Artist: &ari, + Role: "artwork", + }, + }, }, { Id: "rowboat", Title: "Rowboat", Type: "single", - Year: 2020, - Artists: []string{ "mellodoot" }, + ReleaseDate: make_date_work("12-Mar-2020"), Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Rowboat.webp", Buylink: "https://arimelody.bandcamp.com/track/rowboat", Links: []AlbumLink{ @@ -256,13 +300,26 @@ var placeholders = []Album{ }, }, 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", Title: "Hello World", Type: "single", - Year: 2019, - Artists: []string{ "mellodoot" }, + ReleaseDate: make_date_work("25-Dec-2019"), Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Hello_World.webp", Buylink: "https://arimelody.bandcamp.com/track/hello-world", Links: []AlbumLink{ @@ -284,13 +341,26 @@ var placeholders = []Album{ }, }, 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", Title: "Sine", Type: "single", - Artists: []string{ "zaire", "mellodoot" }, - Year: 2019, + ReleaseDate: make_date_work("07-Dec-2019"), Artwork: "https://mellodoot.com/img/music_artwork/zaire_-_Sine_ft._mellodoot.webp", Links: []AlbumLink{ { @@ -306,13 +376,22 @@ var placeholders = []Album{ Url: "https://www.youtube.com/watch?v=z1H1s6VRnyY", }, }, + Credits: []AlbumCredit{ + AlbumCredit{ + Artist: &zaire, + Role: "production", + }, + AlbumCredit{ + Artist: &ari, + Role: "production", + }, + }, }, { Id: "10", Title: "10", Type: "single", - Year: 2019, - Artists: []string{ "mellodoot" }, + ReleaseDate: make_date_work("29-Sep-2019"), Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_10.webp", Buylink: "https://arimelody.bandcamp.com/track/10", Links: []AlbumLink{ @@ -333,13 +412,22 @@ var placeholders = []Album{ Url: "https://www.youtube.com/watch?v=C7WP5L2BK4U", }, }, + Credits: []AlbumCredit{ + AlbumCredit{ + Artist: &ari, + Role: "production", + }, + AlbumCredit{ + Artist: &ari, + Role: "artwork", + }, + }, }, { Id: "mad", Title: "MAD", Type: "single", - Year: 2018, - Artists: []string{ "mellodoot" }, + ReleaseDate: make_date_work("03-Nov-2018"), Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_MAD.webp", Buylink: "https://arimelody.bandcamp.com/track/mad", Links: []AlbumLink{ @@ -360,13 +448,26 @@ var placeholders = []Album{ 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", Title: "Welcoming Party", Type: "album", - Year: 2018, - Artists: []string{ "mellodoot" }, + ReleaseDate: make_date_work("01-Nov-2018"), Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Welcoming_Party.webp", Buylink: "https://arimelody.bandcamp.com/album/welcoming-party", Links: []AlbumLink{ @@ -387,6 +488,16 @@ var placeholders = []Album{ Url: "https://www.youtube.com/playlist?list=PLBG_QJeOHrB5EeniiXBIlHpoQbD6CUJca", }, }, + Credits: []AlbumCredit{ + AlbumCredit{ + Artist: &ari, + Role: "production", + }, + AlbumCredit{ + Artist: &ari, + Role: "artwork", + }, + }, // "tracks": [ // { // "title": "Paradigm" @@ -409,11 +520,10 @@ var placeholders = []Album{ Id: "howtheyknow2018", Title: "How They Know (2018)", Type: "single", - Year: 2018, - Artists: []string{ "mellodoot" }, + ReleaseDate: make_date_work("27-Feb-2018"), 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", - Free: true, Links: []AlbumLink{ { Name: "soundcloud", @@ -424,16 +534,25 @@ var placeholders = []Album{ Url: "https://www.youtube.com/watch?v=mbAgSwCzyMw", }, }, + Credits: []AlbumCredit{ + AlbumCredit{ + Artist: &ari, + Role: "production", + }, + AlbumCredit{ + Artist: &red, + Role: "artwork", + }, + }, }, { Id: "howtheyknow", Title: "How They Know", Type: "single", - Year: 2017, - Artists: []string{ "mellodoot" }, + ReleaseDate: make_date_work("29-Nov-2017"), Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_How_They_Know.webp", + Buyname: "free download", Buylink: "https://arimelody.bandcamp.com/track/how-they-know", - Free: true, Links: []AlbumLink{ { Name: "soundcloud", @@ -444,16 +563,25 @@ var placeholders = []Album{ Url: "https://www.youtube.com/watch?v=q6lzKuG1Emo", }, }, + Credits: []AlbumCredit{ + AlbumCredit{ + Artist: &ari, + Role: "production", + }, + AlbumCredit{ + Artist: &red, + Role: "artwork", + }, + }, }, { Id: "traveller", Title: "Traveller", Type: "single", - Year: 2017, - Artists: []string{ "mellodoot" }, + ReleaseDate: make_date_work("24-Sep-2017"), Artwork: "https://mellodoot.com/img/music_artwork/mellodoot_-_Traveller.webp", + Buyname: "free download", Buylink: "https://arimelody.bandcamp.com/track/traveller", - Free: true, Links: []AlbumLink{ { Name: "soundcloud", @@ -465,6 +593,16 @@ var placeholders = []Album{ }, }, 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 } -func QueryAll() ([]Album) { +func QueryAllAlbums() ([]Album) { return placeholders } +func QueryAllArtists() ([]Artist) { + return []Artist{ ari, zaire, mae, loudar, red } +} + diff --git a/api/v1/music/music_types.go b/api/v1/music/music_types.go index f42224d..b22a423 100644 --- a/api/v1/music/music_types.go +++ b/api/v1/music/music_types.go @@ -3,19 +3,23 @@ package music import ( "regexp" "strings" + "time" ) type ( + Artist struct { + Name string; + Website string; + } + Album struct { Id string; Title string; Type string; - Year int; - Artists []string; + ReleaseDate time.Time; Artwork string; Buyname string; Buylink string; - Free bool; Links []AlbumLink; Description string; Credits []AlbumCredit; @@ -28,26 +32,59 @@ type ( } AlbumCredit struct { + Artist *Artist; Role string; - Name string; - Url string; } ) -func (album Album) PrintArtists() string { - if len(album.Artists) == 1 { - return album.Artists[0] +func (album Album) GetUniqueArtists() []Artist { + if len(album.Credits) == 1 { + 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 } func (album Album) PrintCommaArtists() string { - if len(album.Artists) == 1 { - return album.Artists[0] + if len(album.Credits) == 1 { + 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 { @@ -57,7 +94,15 @@ func (album Album) ResolveArtwork() string { return "/img/music-artwork/default.png" } +func (album Album) GetReleaseYear() int { + return album.ReleaseDate.Year() +} + func (link AlbumLink) NormaliseName() string { re := regexp.MustCompile(`[^a-z0-9]`) return strings.ToLower(re.ReplaceAllString(link.Name, "")) } + +func (credit AlbumCredit) ResolveArtist() Artist { + return *credit.Artist +} diff --git a/db.go b/db.go new file mode 100644 index 0000000..e8eb50c --- /dev/null +++ b/db.go @@ -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 +} diff --git a/go.mod b/go.mod index 4a5b235..a20b06c 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,15 @@ module arimelody.me/arimelody.me 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 +) diff --git a/go.sum b/go.sum index 0303439..83d5b57 100644 --- a/go.sum +++ b/go.sum @@ -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/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= diff --git a/main.go b/main.go index 4a7792a..4d693b7 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( "log" "strings" "time" + "strconv" "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/html" @@ -35,49 +36,69 @@ var templates = template.Must(template.ParseFiles( "views/music-gateway.html", )) -func web_handler(writer http.ResponseWriter, req *http.Request) { - uri := req.URL.Path - - fmt.Printf("[%s] %s %s (%s)\n", - time.Now().Format(time.UnixDate), +func log_request(req *http.Request, code int, start_time time.Time) { + 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 - %d (%sms) (%s)\n", + now.Format(time.UnixDate), req.Method, req.URL.Path, + code, + elapsed, 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 == "/" { - index_handler(writer, req) + code := index_handler(writer, req) + log_request(req, code, start_time) return } if uri == "/music" { - music_handler(writer, req) + code := music_handler(writer, req) + log_request(req, code, start_time) return } if strings.HasPrefix(uri, "/music/") { - music_gateway_handler(writer, req) + code := music_gateway_handler(writer, req) + log_request(req, code, start_time) 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) if err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) + return 500 } + return 200 } -func music_handler(writer http.ResponseWriter, req *http.Request) { - err := templates.ExecuteTemplate(writer, "music.html", music.QueryAll()) +func music_handler(writer http.ResponseWriter, req *http.Request) int { + err := templates.ExecuteTemplate(writer, "music.html", music.QueryAllAlbums()) if err != nil { 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/") { http.Error(writer, "400 bad request", http.StatusBadRequest) - return + return 400 } 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) if !ok { http.Error(writer, "404 not found", http.StatusNotFound) - return + return 404 } err := templates.ExecuteTemplate(writer, "music-gateway.html", album) if err != nil { 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:] - body, err := os.ReadFile(filename) + + // check the file's metadata + info, err := os.Stat(filename) if err != nil { 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: , :: 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:] if mime_type, ok := mime_types[filetype]; ok { writer.Header().Set("Content-Type", mime_type) } else { writer.Header().Set("Content-Type", "text/plain; charset=utf-8") } + writer.Write([]byte(body)) + return 200 } func parse_markdown(md []byte) []byte { @@ -122,7 +176,23 @@ func parse_markdown(md []byte) []byte { 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() { + // push_to_db_this_is_a_testing_thing_and_will_be_superfluous_later() + http.HandleFunc("/", web_handler) log.Fatal(http.ListenAndServe(":8080", nil)) } diff --git a/public/style/music-gateway.css b/public/style/music-gateway.css index b783e3b..866af9a 100644 --- a/public/style/music-gateway.css +++ b/public/style/music-gateway.css @@ -216,7 +216,7 @@ div#info { #title-container { display: flex; - align-items: baseline; + align-items: last baseline; flex-direction: row; } diff --git a/views/music-gateway.html b/views/music-gateway.html index 1e92436..dd582a8 100644 --- a/views/music-gateway.html +++ b/views/music-gateway.html @@ -11,7 +11,7 @@ - + @@ -61,23 +61,26 @@

{{.Title}}

- {{.Year}} + {{.GetReleaseYear}}

{{.PrintArtists}}

{{.Type}}

+ {{if .Description}} +

+ {{.Description}} +

+ {{end}} + -

- {{.Description}} -

- + {{if or .Credits .Lyrics}} + {{end}}

share

@@ -110,7 +111,8 @@

credits: