/*
Site generator for gemini://gemini.tunerapp.org
Copyright (C) 2021 Louis Brauer <louis77@members.fsf.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package main
import (
"io"
"log"
"strings"
"text/template"
"time"
"git.sr.ht/~louis77/gemini.tunerapp.org/handlers"
"github.com/ilk/gobcache"
"github.com/kahlys/flagenv"
"github.com/pitr/gig"
"gitlab.com/AgentNemo/goradios"
)
type radioBrowserData struct {
Stations []goradios.Station
Top5Stations []goradios.Station
Countries []goradios.Country
Languages []goradios.Language
}
var (
data radioBrowserData
refreshDataFlag = flagenv.Bool("refresh", "REFRESH", false, "Refresh cached radio-browser data")
helpers = template.FuncMap{
// The name "title" is what the function will be called in the template text.
"escape": handlers.URLEscape,
"split": strings.Split,
"timeformat": handlers.TimeFormat,
"durationformat": handlers.DurationFormat,
}
)
func loadData(refresh bool) {
cacheName := "cache"
cache := gobcache.NewCache(gobcache.Config{})
if !refresh {
if err := cache.GetData(cacheName, &data); err == nil {
log.Println("Data loaded from local cache")
return
}
}
started := time.Now()
log.Println("Loading data from radio-browser...")
log.Println("... stations")
data.Stations = goradios.FetchAllStationsDetailed(goradios.StationsOrderName, false, 0, 0, true)
go func() {
for {
log.Println("... top 5 stations with refresh every minute")
data.Top5Stations = goradios.FetchAllStationsDetailed(goradios.StationsOrderLastCheckTime, true, 0, 5, true)
time.Sleep(1 * time.Minute)
}
}()
log.Println("... countries")
data.Countries = goradios.FetchCountriesDetailed(goradios.OrderName, false, true)
log.Println("... languages")
data.Languages = goradios.FetchLanguagesDetailed(goradios.OrderName, false, true)
log.Println("saving to cache")
if err := cache.SaveData(cacheName, data); err != nil {
log.Printf("error: %v", err)
log.Println("Cache not saved, will continue with in-memory data")
}
log.Printf("finished in %fs\n", time.Since(started).Seconds())
}
type templatesType struct {
templates *template.Template
}
func (t *templatesType) Render(w io.Writer, name string, data interface{}, c gig.Context) error {
return t.templates.Funcs(helpers).ExecuteTemplate(w, name, data)
}
func main() {
printLicense()
flagenv.Parse()
loadData(*refreshDataFlag)
g := gig.Default()
g.Renderer = &templatesType{
templates: template.Must(template.New("index").Funcs(helpers).ParseGlob("templates/*.tmpl")),
}
g.Handle("/favicon.txt", func(c gig.Context) error {
return c.File("static/favicon.txt")
})
g.Static("/pages", "pages")
g.Handle("/station-image/:id", handlers.HandleStationImage(data.Stations))
g.Handle("/station-pin/:id", handlers.HandleStationPin(data.Stations))
g.Handle("/", handlers.HandleIndex(data.Stations, data.Top5Stations))
g.Handle("/all-stations.gmi", handlers.HandleStations(data.Stations))
g.Handle("/countries.gmi", handlers.HandleCountries(data.Countries))
g.Handle("/countries/:name", handlers.HandleCountry(data.Countries, data.Stations))
g.Handle("/stations/:id", handlers.HandleStation(data.Stations))
g.Handle("/search", handlers.HandleSearch(data.Stations))
g.Handle("/languages.gmi", handlers.HandleLanguages(data.Languages))
// Start server on PORT or default port
log.Fatal(g.Run("cert.pem", "key.rsa"))
}