delete oklog

This commit is contained in:
sentriz
2023-10-04 20:45:41 +01:00
parent e842b896ec
commit a669ba8598
5 changed files with 190 additions and 126 deletions

View File

@@ -1,7 +1,8 @@
//nolint:lll,gocyclo,forbidigo //nolint:lll,gocyclo,forbidigo,nilerr
package main package main
import ( import (
"context"
"errors" "errors"
"expvar" "expvar"
"flag" "flag"
@@ -9,10 +10,12 @@ import (
"log" "log"
"net/http" "net/http"
"os" "os"
"os/signal"
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"syscall"
"time" "time"
// avatar encode/decode // avatar encode/decode
@@ -22,9 +25,9 @@ import (
"github.com/google/shlex" "github.com/google/shlex"
"github.com/gorilla/securecookie" "github.com/gorilla/securecookie"
_ "github.com/jinzhu/gorm/dialects/sqlite" _ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/oklog/run"
"github.com/peterbourgon/ff" "github.com/peterbourgon/ff"
"github.com/sentriz/gormstore" "github.com/sentriz/gormstore"
"golang.org/x/sync/errgroup"
"go.senan.xyz/gonic" "go.senan.xyz/gonic"
"go.senan.xyz/gonic/db" "go.senan.xyz/gonic/db"
@@ -51,7 +54,7 @@ func main() {
confTLSCert := set.String("tls-cert", "", "path to TLS certificate (optional)") confTLSCert := set.String("tls-cert", "", "path to TLS certificate (optional)")
confTLSKey := set.String("tls-key", "", "path to TLS private key (optional)") confTLSKey := set.String("tls-key", "", "path to TLS private key (optional)")
confPodcastPurgeAgeDays := set.Int("podcast-purge-age", 0, "age (in days) to purge podcast episodes if not accessed (optional)") confPodcastPurgeAgeDays := set.Uint("podcast-purge-age", 0, "age (in days) to purge podcast episodes if not accessed (optional)")
confPodcastPath := set.String("podcast-path", "", "path to podcasts") confPodcastPath := set.String("podcast-path", "", "path to podcasts")
confCachePath := set.String("cache-path", "", "path to cache") confCachePath := set.String("cache-path", "", "path to cache")
@@ -63,7 +66,7 @@ func main() {
confDBPath := set.String("db-path", "gonic.db", "path to database (optional)") confDBPath := set.String("db-path", "gonic.db", "path to database (optional)")
confScanIntervalMins := set.Int("scan-interval", 0, "interval (in minutes) to automatically scan music (optional)") confScanIntervalMins := set.Uint("scan-interval", 0, "interval (in minutes) to automatically scan music (optional)")
confScanAtStart := set.Bool("scan-at-start-enabled", false, "whether to perform an initial scan at startup (optional)") confScanAtStart := set.Bool("scan-at-start-enabled", false, "whether to perform an initial scan at startup (optional)")
confScanWatcher := set.Bool("scan-watcher-enabled", false, "whether to watch file system for new music and rescan (optional)") confScanWatcher := set.Bool("scan-watcher-enabled", false, "whether to watch file system for new music and rescan (optional)")
@@ -263,7 +266,6 @@ func main() {
expvar.Publish("stats", expvar.Func(func() any { expvar.Publish("stats", expvar.Func(func() any {
var stats struct{ Albums, Tracks, Artists, InternetRadioStations, Podcasts uint } var stats struct{ Albums, Tracks, Artists, InternetRadioStations, Podcasts uint }
dbc.Model(db.Album{}).Count(&stats.Albums) dbc.Model(db.Album{}).Count(&stats.Albums)
dbc.Model(db.Track{}).Count(&stats.Tracks)
dbc.Model(db.Artist{}).Count(&stats.Artists) dbc.Model(db.Artist{}).Count(&stats.Artists)
dbc.Model(db.InternetRadioStation{}).Count(&stats.InternetRadioStations) dbc.Model(db.InternetRadioStation{}).Count(&stats.InternetRadioStations)
dbc.Model(db.Podcast{}).Count(&stats.Podcasts) dbc.Model(db.Podcast{}).Count(&stats.Podcasts)
@@ -271,122 +273,184 @@ func main() {
})) }))
} }
noCleanup := func(_ error) {} errgrp, ctx := errgroup.WithContext(context.Background())
var g run.Group errgrp.Go(func() error {
g.Add(func() error { defer logJob("http")()
log.Print("starting job 'http'\n")
server := &http.Server{ server := &http.Server{Addr: *confListenAddr, Handler: mux, ReadHeaderTimeout: 5 * time.Second}
Addr: *confListenAddr, errgrp.Go(func() error {
Handler: mux, <-ctx.Done()
ReadTimeout: 5 * time.Second, return server.Shutdown(context.Background())
ReadHeaderTimeout: 5 * time.Second, })
WriteTimeout: 80 * time.Second,
IdleTimeout: 60 * time.Second,
}
if *confTLSCert != "" && *confTLSKey != "" { if *confTLSCert != "" && *confTLSKey != "" {
return server.ListenAndServeTLS(*confTLSCert, *confTLSKey) return server.ListenAndServeTLS(*confTLSCert, *confTLSKey)
} }
return server.ListenAndServe() return server.ListenAndServe()
}, noCleanup) })
errgrp.Go(func() error {
defer logJob("session clean")()
g.Add(func() error {
log.Printf("starting job 'session clean'\n")
ticker := time.NewTicker(10 * time.Minute) ticker := time.NewTicker(10 * time.Minute)
for range ticker.C { select {
case <-ctx.Done():
return nil
case <-ticker.C:
sessDB.Cleanup() sessDB.Cleanup()
} }
return nil return nil
}, noCleanup) })
errgrp.Go(func() error {
defer logJob("podcast refresh")()
g.Add(func() error {
log.Printf("starting job 'podcast refresher'\n")
ticker := time.NewTicker(time.Hour) ticker := time.NewTicker(time.Hour)
for range ticker.C { select {
case <-ctx.Done():
return nil
case <-ticker.C:
if err := podcast.RefreshPodcasts(); err != nil { if err := podcast.RefreshPodcasts(); err != nil {
log.Printf("failed to refresh some feeds: %s", err) log.Printf("failed to refresh some feeds: %s", err)
} }
} }
return nil return nil
}, noCleanup) })
if *confPodcastPurgeAgeDays > 0 { errgrp.Go(func() error {
g.Add(func() error { if *confPodcastPurgeAgeDays == 0 {
log.Printf("starting job 'podcast purger'\n")
ticker := time.NewTicker(24 * time.Hour)
for range ticker.C {
if err := podcast.PurgeOldPodcasts(time.Duration(*confPodcastPurgeAgeDays) * 24 * time.Hour); err != nil {
log.Printf("error purging old podcasts: %v", err)
}
}
return nil return nil
}, noCleanup)
}
if *confScanIntervalMins > 0 {
g.Add(func() error {
log.Printf("starting job 'scan timer'\n")
ticker := time.NewTicker(time.Duration(*confScanIntervalMins) * time.Minute)
for range ticker.C {
if _, err := scannr.ScanAndClean(scanner.ScanOptions{}); err != nil {
log.Printf("error scanning: %v", err)
}
}
return nil
}, noCleanup)
}
if *confScanWatcher {
g.Add(func() error {
log.Printf("starting job 'scan watcher'\n")
return scannr.ExecuteWatch()
}, func(_ error) {
scannr.CancelWatch()
})
}
if jukebx != nil {
var jukeboxTempDir string
g.Add(func() error {
log.Printf("starting job 'jukebox'\n")
extraArgs, _ := shlex.Split(*confJukeboxMPVExtraArgs)
var err error
jukeboxTempDir, err = os.MkdirTemp("", "gonic-jukebox-*")
if err != nil {
return fmt.Errorf("create tmp sock file: %w", err)
}
sockPath := filepath.Join(jukeboxTempDir, "sock")
if err := jukebx.Start(sockPath, extraArgs); err != nil {
return fmt.Errorf("start jukebox: %w", err)
}
if err := jukebx.Wait(); err != nil {
return fmt.Errorf("start jukebox: %w", err)
}
return nil
}, func(_ error) {
if err := jukebx.Quit(); err != nil {
log.Printf("error quitting jukebox: %v", err)
}
_ = os.RemoveAll(jukeboxTempDir)
})
}
if _, _, err := lastfmClientKeySecretFunc(); err == nil {
g.Add(func() error {
log.Printf("starting job 'refresh artist info'\n")
return artistInfoCache.Refresh(8 * time.Second)
}, noCleanup)
}
if *confScanAtStart {
if _, err := scannr.ScanAndClean(scanner.ScanOptions{}); err != nil {
log.Panicf("error scanning at start: %v\n", err)
} }
defer logJob("podcast purge")()
ticker := time.NewTicker(24 * time.Hour)
select {
case <-ctx.Done():
return nil
case <-ticker.C:
if err := podcast.PurgeOldPodcasts(time.Duration(*confPodcastPurgeAgeDays) * 24 * time.Hour); err != nil {
log.Printf("error purging old podcasts: %v", err)
}
}
return nil
})
errgrp.Go(func() error {
if *confScanIntervalMins == 0 {
return nil
}
defer logJob("scan timer")()
ticker := time.NewTicker(time.Duration(*confScanIntervalMins) * time.Minute)
select {
case <-ctx.Done():
return nil
case <-ticker.C:
if _, err := scannr.ScanAndClean(scanner.ScanOptions{}); err != nil {
log.Printf("error scanning: %v", err)
}
}
return nil
})
errgrp.Go(func() error {
if !*confScanWatcher {
return nil
}
defer logJob("scan watcher")()
errgrp.Go(func() error {
<-ctx.Done()
scannr.CancelWatch()
return nil
})
return scannr.ExecuteWatch()
})
errgrp.Go(func() error {
if jukebx == nil {
return nil
}
defer logJob("jukebox")()
extraArgs, _ := shlex.Split(*confJukeboxMPVExtraArgs)
var err error
cacheDir, err := os.UserCacheDir()
if err != nil {
return fmt.Errorf("get user cache dir: %w", err)
}
jukeboxTempDir := filepath.Join(cacheDir, "gonic-jukebox")
if err := os.RemoveAll(jukeboxTempDir); err != nil {
return fmt.Errorf("remove jubebox tmp dir: %w", err)
}
if err := os.MkdirAll(jukeboxTempDir, os.ModePerm); err != nil {
return fmt.Errorf("create tmp sock file: %w", err)
}
sockPath := filepath.Join(jukeboxTempDir, "sock")
if err := jukebx.Start(sockPath, extraArgs); err != nil {
return fmt.Errorf("start jukebox: %w", err)
}
errgrp.Go(func() error {
<-ctx.Done()
return jukebx.Quit()
})
if err := jukebx.Wait(); err != nil {
return fmt.Errorf("start jukebox: %w", err)
}
return nil
})
errgrp.Go(func() error {
if _, _, err := lastfmClientKeySecretFunc(); err != nil {
return nil
}
defer logJob("refresh artist info")()
ticker := time.NewTicker(8 * time.Second)
select {
case <-ctx.Done():
case <-ticker.C:
if err := artistInfoCache.Refresh(); err != nil {
log.Printf("error in artist info cache: %v", err)
}
}
return nil
})
errgrp.Go(func() error {
if !*confScanAtStart {
return nil
}
defer logJob("scan at start")()
_, err := scannr.ScanAndClean(scanner.ScanOptions{})
return err
})
errShutdown := errors.New("shutdown")
errgrp.Go(func() error {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
select {
case <-ctx.Done():
return nil
case <-sigChan:
return errShutdown
}
})
if err := errgrp.Wait(); err != nil && !errors.Is(err, errShutdown) {
log.Panic(err)
} }
if err := g.Run(); err != nil { fmt.Println("shutdown complete")
log.Panicf("error in job: %v", err)
}
} }
const pathAliasSep = "->" const pathAliasSep = "->"
@@ -461,3 +525,8 @@ func (mvs *multiValueSetting) Set(value string) error {
} }
return nil return nil
} }
func logJob(jobName string) func() {
log.Printf("starting job %q", jobName)
return func() { log.Printf("stopped job %q", jobName) }
}

2
go.mod
View File

@@ -20,7 +20,6 @@ require (
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/mmcdole/gofeed v1.2.1 github.com/mmcdole/gofeed v1.2.1
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/oklog/run v1.1.0
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c
github.com/peterbourgon/ff v1.7.1 github.com/peterbourgon/ff v1.7.1
github.com/philippta/go-template v0.0.0-20220911145045-4556aca435e4 github.com/philippta/go-template v0.0.0-20220911145045-4556aca435e4
@@ -30,6 +29,7 @@ require (
github.com/stretchr/testify v1.8.1 github.com/stretchr/testify v1.8.1
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 golang.org/x/exp v0.0.0-20230905200255-921286631fa9
golang.org/x/net v0.15.0 golang.org/x/net v0.15.0
golang.org/x/sync v0.3.0
gopkg.in/gormigrate.v1 v1.6.0 gopkg.in/gormigrate.v1 v1.6.0
jaytaylor.com/html2text v0.0.0-20230321000545-74c2419ad056 jaytaylor.com/html2text v0.0.0-20230321000545-74c2419ad056
) )

4
go.sum
View File

@@ -119,8 +119,6 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
@@ -184,6 +182,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -310,9 +310,9 @@ func (j *Jukebox) Quit() error {
if j.conn == nil || j.conn.IsClosed() { if j.conn == nil || j.conn.IsClosed() {
return nil return nil
} }
if _, err := j.conn.Call("quit"); err != nil { go func() {
return fmt.Errorf("quit: %w", err) j.conn.Call("quit")
} }()
if err := j.conn.Close(); err != nil { if err := j.conn.Close(); err != nil {
return fmt.Errorf("close: %w", err) return fmt.Errorf("close: %w", err)
} }

View File

@@ -94,29 +94,24 @@ func (a *ArtistInfoCache) Lookup(ctx context.Context, artist *db.Artist) (*db.Ar
return &artistInfo, nil return &artistInfo, nil
} }
func (a *ArtistInfoCache) Refresh(interval time.Duration) error { func (a *ArtistInfoCache) Refresh() error {
ticker := time.NewTicker(interval) q := a.db.
for range ticker.C { Where("artist_infos.id IS NULL OR artist_infos.updated_at<?", time.Now().Add(-keepFor)).
q := a.db. Joins("LEFT JOIN artist_infos ON artist_infos.id=artists.id")
Where("artist_infos.id IS NULL OR artist_infos.updated_at<?", time.Now().Add(-keepFor)).
Joins("LEFT JOIN artist_infos ON artist_infos.id=artists.id")
var artist db.Artist var artist db.Artist
if err := q.Find(&artist).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { if err := q.Find(&artist).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
log.Printf("error finding non cached artist: %v", err) return fmt.Errorf("finding non cached artist: %w", err)
continue
}
if artist.ID == 0 {
continue
}
if _, err := a.Lookup(context.Background(), &artist); err != nil {
log.Printf("error looking up non cached artist %s: %v", artist.Name, err)
continue
}
log.Printf("cached artist info for %q", artist.Name)
} }
if artist.ID == 0 {
return nil
}
if _, err := a.Lookup(context.Background(), &artist); err != nil {
return fmt.Errorf("looking up non cached artist %s: %w", artist.Name, err)
}
log.Printf("cached artist info for %q", artist.Name)
return nil return nil
} }