whoever was responsible for that indentation blunder, you're fired

Signed-off-by: ari melody <ari@arimelody.me>
This commit is contained in:
ari melody 2024-03-23 18:02:11 +00:00
parent cddd5656f2
commit 63221e9fd2
5 changed files with 937 additions and 934 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,172 +1,172 @@
package music package music
import ( import (
"regexp" "regexp"
"strings" "strings"
"time" "time"
) )
type ( type (
Artist struct { Artist struct {
Id string Id string
Name string Name string
Website string Website string
} }
MusicRelease struct { MusicRelease struct {
Id string Id string
Title string Title string
Type string Type string
ReleaseDate time.Time ReleaseDate time.Time
Artwork string Artwork string
Buyname string Buyname string
Buylink string Buylink string
Links []MusicLink Links []MusicLink
Description string Description string
Credits []MusicCredit Credits []MusicCredit
Lyrics string Lyrics string
} }
MusicLink struct { MusicLink struct {
Name string Name string
Url string Url string
} }
MusicCredit struct { MusicCredit struct {
Artist *Artist Artist *Artist
Role string Role string
Meta bool // for "meta" contributors (i.e. not credited for the musical work, but other related assets) Meta bool // for "meta" contributors (i.e. not credited for the musical work, but other related assets)
} }
) )
func (album MusicRelease) GetUniqueArtists() []Artist { func (album MusicRelease) GetUniqueArtists() []Artist {
if len(album.Credits) == 1 { if len(album.Credits) == 1 {
return []Artist{*album.Credits[0].Artist} return []Artist{*album.Credits[0].Artist}
} }
// create a map of artists to prevent duplicates // create a map of artists to prevent duplicates
res := []Artist{} res := []Artist{}
for _, credit := range album.Credits { for _, credit := range album.Credits {
artist := *credit.Artist artist := *credit.Artist
exists := false exists := false
for _, c := range res { for _, c := range res {
if c == *credit.Artist { if c == *credit.Artist {
exists = true exists = true
break break
} }
} }
if exists { if exists {
continue continue
} }
res = append(res, artist) res = append(res, artist)
} }
// now create the actual array to send // now create the actual array to send
return res return res
} }
func (album MusicRelease) GetUniqueNonMetaArtists() []Artist { func (album MusicRelease) GetUniqueNonMetaArtists() []Artist {
if len(album.Credits) == 1 { if len(album.Credits) == 1 {
return []Artist{*album.Credits[0].Artist} return []Artist{*album.Credits[0].Artist}
} }
// create a map of artists to prevent duplicates // create a map of artists to prevent duplicates
res := []Artist{} res := []Artist{}
for _, credit := range album.Credits { for _, credit := range album.Credits {
if credit.Meta { if credit.Meta {
continue continue
} }
artist := *credit.Artist artist := *credit.Artist
exists := false exists := false
for _, c := range res { for _, c := range res {
if c == *credit.Artist { if c == *credit.Artist {
exists = true exists = true
break break
} }
} }
if exists { if exists {
continue continue
} }
res = append(res, artist) res = append(res, artist)
} }
// now create the actual array to send // now create the actual array to send
return res return res
} }
func (album MusicRelease) GetUniqueArtistNames() []string { func (album MusicRelease) GetUniqueArtistNames() []string {
if len(album.Credits) == 1 { if len(album.Credits) == 1 {
return []string{album.Credits[0].Artist.Name} return []string{album.Credits[0].Artist.Name}
} }
artists := album.GetUniqueArtists() artists := album.GetUniqueArtists()
names := []string{} names := []string{}
for _, artist := range artists { for _, artist := range artists {
names = append(names, artist.Name) names = append(names, artist.Name)
} }
return names return names
} }
func (album MusicRelease) GetUniqueNonMetaArtistNames() []string { func (album MusicRelease) GetUniqueNonMetaArtistNames() []string {
if len(album.Credits) == 1 { if len(album.Credits) == 1 {
return []string{album.Credits[0].Artist.Name} return []string{album.Credits[0].Artist.Name}
} }
artists := album.GetUniqueNonMetaArtists() artists := album.GetUniqueNonMetaArtists()
names := []string{} names := []string{}
for _, artist := range artists { for _, artist := range artists {
names = append(names, artist.Name) names = append(names, artist.Name)
} }
return names return names
} }
func (album MusicRelease) PrintPrimaryArtists() string { func (album MusicRelease) PrintPrimaryArtists() string {
names := album.GetUniqueNonMetaArtistNames() names := album.GetUniqueNonMetaArtistNames()
if len(names) == 1 { if len(names) == 1 {
return names[0] return names[0]
} }
res := strings.Join(names[:len(names)-1], ", ") res := strings.Join(names[:len(names)-1], ", ")
res += " & " + names[len(names)-1] res += " & " + names[len(names)-1]
return res return res
} }
func (album MusicRelease) PrintCommaPrimaryArtists() string { func (album MusicRelease) PrintCommaPrimaryArtists() string {
names := album.GetUniqueNonMetaArtistNames() names := album.GetUniqueNonMetaArtistNames()
if len(names) == 1 { if len(names) == 1 {
return names[0] return names[0]
} }
return strings.Join(names[:], ", ") return strings.Join(names[:], ", ")
} }
func (album MusicRelease) ResolveType() string { func (album MusicRelease) ResolveType() string {
if album.Type != "" { if album.Type != "" {
return album.Type return album.Type
} }
return "unknown" return "unknown"
} }
func (album MusicRelease) ResolveArtwork() string { func (album MusicRelease) ResolveArtwork() string {
if album.Artwork != "" { if album.Artwork != "" {
return album.Artwork return album.Artwork
} }
return "/img/music-artwork/default.png" return "/img/music-artwork/default.png"
} }
func (album MusicRelease) PrintReleaseDate() string { func (album MusicRelease) PrintReleaseDate() string {
return album.ReleaseDate.Format("02 January 2006") return album.ReleaseDate.Format("02 January 2006")
} }
func (album MusicRelease) GetReleaseYear() int { func (album MusicRelease) GetReleaseYear() int {
return album.ReleaseDate.Year() return album.ReleaseDate.Year()
} }
func (link MusicLink) NormaliseName() string { func (link MusicLink) 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 MusicCredit) ResolveArtist() Artist { func (credit MusicCredit) ResolveArtist() Artist {
return *credit.Artist return *credit.Artist
} }

120
db.go
View file

@ -1,97 +1,97 @@
package main package main
import ( import (
"arimelody.me/arimelody.me/api/v1/music" "arimelody.me/arimelody.me/api/v1/music"
"fmt" "fmt"
"os" "os"
"time" "time"
_ "github.com/lib/pq" _ "github.com/lib/pq"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
) )
var schema = var schema =
`CREATE TABLE IF NOT EXISTS artists ( `CREATE TABLE IF NOT EXISTS artists (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
name TEXT, name TEXT,
website TEXT website TEXT
); );
CREATE TABLE IF NOT EXISTS musicreleases ( CREATE TABLE IF NOT EXISTS musicreleases (
id VARCHAR(64) PRIMARY KEY, id VARCHAR(64) PRIMARY KEY,
title TEXT NOT NULL, title TEXT NOT NULL,
type TEXT, type TEXT,
release_date DATE NOT NULL, release_date DATE NOT NULL,
artwork TEXT, artwork TEXT,
buyname TEXT, buyname TEXT,
buylink TEXT, buylink TEXT,
description TEXT, description TEXT,
lyrics TEXT lyrics TEXT
); );
CREATE TABLE IF NOT EXISTS musiclinks ( CREATE TABLE IF NOT EXISTS musiclinks (
album VARCHAR(64) REFERENCES musicreleases(id) ON DELETE CASCADE ON UPDATE CASCADE, album VARCHAR(64) REFERENCES musicreleases(id) ON DELETE CASCADE ON UPDATE CASCADE,
name TEXT, name TEXT,
url TEXT, url TEXT,
CONSTRAINT musiclinks_pk PRIMARY KEY (album, name) CONSTRAINT musiclinks_pk PRIMARY KEY (album, name)
); );
CREATE TABLE IF NOT EXISTS musiccredits ( CREATE TABLE IF NOT EXISTS musiccredits (
album VARCHAR(64) REFERENCES musicreleases(ID) ON DELETE CASCADE, album VARCHAR(64) REFERENCES musicreleases(ID) ON DELETE CASCADE,
artist TEXT REFERENCES artists(id) ON DELETE CASCADE, artist TEXT REFERENCES artists(id) ON DELETE CASCADE,
role TEXT, role TEXT,
meta BOOLEAN, meta BOOLEAN,
constraint musiccredits_pk PRIMARY KEY (album, artist, role) constraint musiccredits_pk PRIMARY KEY (album, artist, role)
);` );`
func PushArtist(db *sqlx.DB, artist music.Artist) { func PushArtist(db *sqlx.DB, artist music.Artist) {
fmt.Printf("syncing artist [%s] to database...", artist.Name) fmt.Printf("syncing artist [%s] to database...", artist.Name)
db.MustExec("INSERT INTO artists (id, name, website) VALUES ($1, $2, $3) ON CONFLICT (id) DO UPDATE SET name=$2, website=$3", db.MustExec("INSERT INTO artists (id, name, website) VALUES ($1, $2, $3) ON CONFLICT (id) DO UPDATE SET name=$2, website=$3",
&artist.Id, &artist.Id,
&artist.Name, &artist.Name,
&artist.Website, &artist.Website,
) )
fmt.Printf("done!\n") fmt.Printf("done!\n")
} }
func PushRelease(db *sqlx.DB, release music.MusicRelease) { func PushRelease(db *sqlx.DB, release music.MusicRelease) {
fmt.Printf("syncing release [%s] to database...", release.Id) fmt.Printf("syncing release [%s] to database...", release.Id)
tx := db.MustBegin() tx := db.MustBegin()
tx.MustExec("INSERT INTO musicreleases (id, title, release_date, artwork, buyname, buylink, description, lyrics) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) "+ tx.MustExec("INSERT INTO musicreleases (id, title, release_date, artwork, buyname, buylink, description, lyrics) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) "+
"ON CONFLICT (id) DO UPDATE SET title=$2, release_date=$3, artwork=$4, buyname=$5, buylink=$6, description=$7, lyrics=$8", "ON CONFLICT (id) DO UPDATE SET title=$2, release_date=$3, artwork=$4, buyname=$5, buylink=$6, description=$7, lyrics=$8",
&release.Id, &release.Title, release.ReleaseDate.Format("2-Jan-2006"), &release.Artwork, &release.Buyname, &release.Buylink, &release.Description, &release.Lyrics) &release.Id, &release.Title, release.ReleaseDate.Format("2-Jan-2006"), &release.Artwork, &release.Buyname, &release.Buylink, &release.Description, &release.Lyrics)
for _, link := range release.Links { for _, link := range release.Links {
tx.MustExec("INSERT INTO musiclinks (album, name, url) VALUES ($1, $2, $3) ON CONFLICT (album, name) DO UPDATE SET url=$3", tx.MustExec("INSERT INTO musiclinks (album, name, url) VALUES ($1, $2, $3) ON CONFLICT (album, name) DO UPDATE SET url=$3",
&release.Id, &link.Name, &link.Url) &release.Id, &link.Name, &link.Url)
} }
for _, credit := range release.Credits { for _, credit := range release.Credits {
tx.MustExec("INSERT INTO musiccredits (album, artist, role, meta) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING", tx.MustExec("INSERT INTO musiccredits (album, artist, role, meta) VALUES ($1, $2, $3, $4) ON CONFLICT DO NOTHING",
&release.Id, &credit.Artist.Id, &credit.Role, &credit.Meta) &release.Id, &credit.Artist.Id, &credit.Role, &credit.Meta)
} }
tx.Commit() tx.Commit()
fmt.Printf("done!\n") fmt.Printf("done!\n")
} }
func InitDatabase() *sqlx.DB { func InitDatabase() *sqlx.DB {
db, err := sqlx.Connect("postgres", "user=arimelody dbname=arimelody password=fuckingpassword sslmode=disable") db, err := sqlx.Connect("postgres", "user=arimelody dbname=arimelody password=fuckingpassword sslmode=disable")
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "unable to create database connection pool: %v\n", err) fmt.Fprintf(os.Stderr, "unable to create database connection pool: %v\n", err)
os.Exit(1) os.Exit(1)
} }
db.SetConnMaxLifetime(time.Minute * 3) db.SetConnMaxLifetime(time.Minute * 3)
db.SetMaxOpenConns(10) db.SetMaxOpenConns(10)
db.SetMaxIdleConns(10) db.SetMaxIdleConns(10)
db.MustExec(schema) db.MustExec(schema)
fmt.Printf("database schema synchronised.\n") fmt.Printf("database schema synchronised.\n")
return db return db
} }

11
go.mod
View file

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

314
main.go
View file

@ -1,220 +1,220 @@
package main package main
import ( import (
"fmt" "fmt"
"html/template" "html/template"
"log" "log"
"net/http" "net/http"
"os" "os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"arimelody.me/arimelody.me/api/v1/music" "arimelody.me/arimelody.me/api/v1/music"
"github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html" "github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser" "github.com/gomarkdown/markdown/parser"
) )
const PORT int = 8080 const PORT int = 8080
var mime_types = map[string]string{ var mime_types = map[string]string{
"css": "text/css; charset=utf-8", "css": "text/css; charset=utf-8",
"png": "image/png", "png": "image/png",
"jpg": "image/jpg", "jpg": "image/jpg",
"webp": "image/webp", "webp": "image/webp",
"html": "text/html", "html": "text/html",
"asc": "text/plain", "asc": "text/plain",
"pub": "text/plain", "pub": "text/plain",
"js": "application/javascript", "js": "application/javascript",
} }
var base_template = template.Must(template.ParseFiles( var base_template = template.Must(template.ParseFiles(
"views/base.html", "views/base.html",
"views/header.html", "views/header.html",
"views/footer.html", "views/footer.html",
"views/prideflag.html", "views/prideflag.html",
)) ))
var htmx_template = template.Must(template.New("root").Parse(`<head>{{block "head" .}}{{end}}</head>{{block "content" .}}{{end}}`)) var htmx_template = template.Must(template.New("root").Parse(`<head>{{block "head" .}}{{end}}</head>{{block "content" .}}{{end}}`))
func log_request(req *http.Request, code int, start_time time.Time) { func log_request(req *http.Request, code int, start_time time.Time) {
now := time.Now() now := time.Now()
difference := (now.Nanosecond() - start_time.Nanosecond()) / 1_000_000 difference := (now.Nanosecond() - start_time.Nanosecond()) / 1_000_000
elapsed := "<1" elapsed := "<1"
if difference >= 1 { if difference >= 1 {
elapsed = strconv.Itoa(difference) elapsed = strconv.Itoa(difference)
} }
fmt.Printf("[%s] %s %s - %d (%sms) (%s)\n", fmt.Printf("[%s] %s %s - %d (%sms) (%s)\n",
now.Format(time.UnixDate), now.Format(time.UnixDate),
req.Method, req.Method,
req.URL.Path, req.URL.Path,
code, code,
elapsed, elapsed,
req.Header["User-Agent"][0], req.Header["User-Agent"][0],
) )
} }
func handle_request(writer http.ResponseWriter, req *http.Request) { func handle_request(writer http.ResponseWriter, req *http.Request) {
uri := req.URL.Path uri := req.URL.Path
start_time := time.Now() start_time := time.Now()
hx_request := len(req.Header["Hx-Request"]) > 0 && req.Header["Hx-Request"][0] == "true" hx_request := len(req.Header["Hx-Request"]) > 0 && req.Header["Hx-Request"][0] == "true"
// don't bother fulfilling requests to a page that's already loaded on the client! // don't bother fulfilling requests to a page that's already loaded on the client!
if hx_request && len(req.Header["Referer"]) > 0 && len(req.Header["Hx-Current-Url"]) > 0 { if hx_request && len(req.Header["Referer"]) > 0 && len(req.Header["Hx-Current-Url"]) > 0 {
regex := regexp.MustCompile(`https?:\/\/[^\/]+`) regex := regexp.MustCompile(`https?:\/\/[^\/]+`)
current_location := regex.ReplaceAllString(req.Header["Hx-Current-Url"][0], "") current_location := regex.ReplaceAllString(req.Header["Hx-Current-Url"][0], "")
if current_location == req.URL.Path { if current_location == req.URL.Path {
writer.WriteHeader(204); writer.WriteHeader(204);
return return
} }
} }
code := func(writer http.ResponseWriter, req *http.Request) int { code := func(writer http.ResponseWriter, req *http.Request) int {
var root *template.Template var root *template.Template
if hx_request { if hx_request {
root = template.Must(htmx_template.Clone()) root = template.Must(htmx_template.Clone())
} else { } else {
root = template.Must(base_template.Clone()) root = template.Must(base_template.Clone())
} }
if req.URL.Path == "/" { if req.URL.Path == "/" {
return index_handler(writer, root) return index_handler(writer, root)
} }
if uri == "/music" || uri == "/music/" { if uri == "/music" || uri == "/music/" {
return music_directory_handler(writer, root) return music_directory_handler(writer, root)
} }
if strings.HasPrefix(uri, "/music/") { if strings.HasPrefix(uri, "/music/") {
return music_gateway_handler(writer, req, root) return music_gateway_handler(writer, req, root)
} }
return static_handler(writer, req) return static_handler(writer, req)
}(writer, req) }(writer, req)
log_request(req, code, start_time) log_request(req, code, start_time)
} }
func index_handler(writer http.ResponseWriter, root *template.Template) int { func index_handler(writer http.ResponseWriter, root *template.Template) int {
index_template := template.Must(root.ParseFiles("views/index.html")) index_template := template.Must(root.ParseFiles("views/index.html"))
err := index_template.Execute(writer, nil) err := index_template.Execute(writer, nil)
if err != nil { if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
return 500 return 500
} }
return 200 return 200
} }
func music_directory_handler(writer http.ResponseWriter, root *template.Template) int { func music_directory_handler(writer http.ResponseWriter, root *template.Template) int {
music_template := template.Must(root.ParseFiles("views/music.html")) music_template := template.Must(root.ParseFiles("views/music.html"))
music := music.QueryAllMusic() music := music.QueryAllMusic()
err := music_template.Execute(writer, music) err := music_template.Execute(writer, music)
if err != nil { if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
return 500 return 500
} }
return 200 return 200
} }
func music_gateway_handler(writer http.ResponseWriter, req *http.Request, root *template.Template) int { func music_gateway_handler(writer http.ResponseWriter, req *http.Request, root *template.Template) int {
id := req.URL.Path[len("/music/"):] id := req.URL.Path[len("/music/"):]
// http.Redirect(writer, req, "https://mellodoot.com/music/"+title, 302) // http.Redirect(writer, req, "https://mellodoot.com/music/"+title, 302)
// return // return
release, ok := music.GetRelease(id) release, ok := music.GetRelease(id)
if !ok { if !ok {
http.Error(writer, "404 not found", http.StatusNotFound) http.Error(writer, "404 not found", http.StatusNotFound)
return 404 return 404
} }
gateway_template := template.Must(root.ParseFiles("views/music-gateway.html")) gateway_template := template.Must(root.ParseFiles("views/music-gateway.html"))
err := gateway_template.Execute(writer, release) err := gateway_template.Execute(writer, release)
if err != nil { if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
return 500 return 500
} }
return 200 return 200
} }
func static_handler(writer http.ResponseWriter, req *http.Request) int { func static_handler(writer http.ResponseWriter, req *http.Request) int {
filename := "public/" + req.URL.Path[1:] filename := "public/" + req.URL.Path[1:]
// check the file's metadata // check the file's metadata
info, err := os.Stat(filename) 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 404 return 404
} }
if len(req.Header["If-Modified-Since"]) > 0 && req.Header["If-Modified-Since"][0] != "" { 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_modified_since_time, err := time.Parse(http.TimeFormat, req.Header["If-Modified-Since"][0])
if err != nil { if err != nil {
http.Error(writer, "400 bad request", http.StatusBadRequest) http.Error(writer, "400 bad request", http.StatusBadRequest)
return 400 return 400
} }
if req.Header["If-Modified-Since"][0] == info.ModTime().Format(http.TimeFormat) || if_modified_since_time.After(info.ModTime()) { if req.Header["If-Modified-Since"][0] == info.ModTime().Format(http.TimeFormat) || if_modified_since_time.After(info.ModTime()) {
writer.WriteHeader(304) // not modified writer.WriteHeader(304) // not modified
return 304 return 304
} }
} }
// set other nice headers // set other nice headers
writer.Header().Set("Cache-Control", "max-age=86400") writer.Header().Set("Cache-Control", "max-age=86400")
writer.Header().Set("Last-Modified", info.ModTime().Format(http.TimeFormat)) writer.Header().Set("Last-Modified", info.ModTime().Format(http.TimeFormat))
// Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT // Last-Modified: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
// Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT // Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
// read the file // read the file
body, err := os.ReadFile(filename) body, err := os.ReadFile(filename)
if err != nil { if err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError) http.Error(writer, err.Error(), http.StatusInternalServerError)
return 500 return 500
} }
// setting MIME types // 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 return 200
} }
func parse_markdown(md []byte) []byte { func parse_markdown(md []byte) []byte {
extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
p := parser.NewWithExtensions(extensions) p := parser.NewWithExtensions(extensions)
doc := p.Parse(md) doc := p.Parse(md)
htmlFlags := html.CommonFlags htmlFlags := html.CommonFlags
opts := html.RendererOptions{Flags: htmlFlags} opts := html.RendererOptions{Flags: htmlFlags}
renderer := html.NewRenderer(opts) renderer := html.NewRenderer(opts)
return markdown.Render(doc, renderer) return markdown.Render(doc, renderer)
} }
func push_to_db_this_is_a_testing_thing_and_will_be_superfluous_later() { func push_to_db_this_is_a_testing_thing_and_will_be_superfluous_later() {
db := InitDatabase() db := InitDatabase()
defer db.Close() defer db.Close()
for _, artist := range music.QueryAllArtists() { for _, artist := range music.QueryAllArtists() {
PushArtist(db, artist) PushArtist(db, artist)
} }
for _, album := range music.QueryAllMusic() { for _, album := range music.QueryAllMusic() {
PushRelease(db, album) PushRelease(db, album)
} }
} }
func main() { func main() {
push_to_db_this_is_a_testing_thing_and_will_be_superfluous_later() push_to_db_this_is_a_testing_thing_and_will_be_superfluous_later()
http.HandleFunc("/", handle_request) http.HandleFunc("/", handle_request)
fmt.Printf("now serving at http://127.0.0.1:%d\n", PORT) fmt.Printf("now serving at http://127.0.0.1:%d\n", PORT)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", PORT), nil)) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", PORT), nil))
} }