11// Package main is the gonic server entrypoint
22//
3- //nolint:lll // flags help strings
3+ //nolint:lll,gocyclo
44package main
55
66import (
77 "errors"
88 "flag"
99 "fmt"
1010 "log"
11+ "net/http"
1112 "os"
1213 "path"
1314 "path/filepath"
@@ -16,19 +17,27 @@ import (
1617 "time"
1718
1819 "github.com/google/shlex"
20+ "github.com/gorilla/mux"
21+ "github.com/gorilla/securecookie"
1922 _ "github.com/jinzhu/gorm/dialects/sqlite"
2023 "github.com/oklog/run"
2124 "github.com/peterbourgon/ff"
25+ "github.com/sentriz/gormstore"
2226
2327 "go.senan.xyz/gonic"
2428 "go.senan.xyz/gonic/db"
29+ "go.senan.xyz/gonic/jukebox"
30+ "go.senan.xyz/gonic/playlist"
31+ "go.senan.xyz/gonic/podcasts"
2532 "go.senan.xyz/gonic/scanner"
26- "go.senan.xyz/gonic/server"
33+ "go.senan.xyz/gonic/scanner/tags"
34+ "go.senan.xyz/gonic/scrobble"
35+ "go.senan.xyz/gonic/scrobble/lastfm"
36+ "go.senan.xyz/gonic/scrobble/listenbrainz"
37+ "go.senan.xyz/gonic/server/ctrladmin"
38+ "go.senan.xyz/gonic/server/ctrlbase"
2739 "go.senan.xyz/gonic/server/ctrlsubsonic"
28- )
29-
30- const (
31- cleanTimeDuration = 10 * time .Minute
40+ "go.senan.xyz/gonic/transcode"
3241)
3342
3443func main () {
@@ -146,53 +155,184 @@ func main() {
146155 * deprecatedConfGenreSplit = "<deprecated>"
147156 }
148157
149- server , err := server .New (server.Options {
150- DB : dbc ,
151- MusicPaths : musicPaths ,
152- ExcludePattern : * confExcludePatterns ,
153- CacheAudioPath : cacheDirAudio ,
154- CoverCachePath : cacheDirCovers ,
155- PodcastPath : * confPodcastPath ,
156- PlaylistsPath : * confPlaylistsPath ,
157- ProxyPrefix : * confProxyPrefix ,
158- MultiValueSettings : map [scanner.Tag ]scanner.MultiValueSetting {
159- scanner .Genre : scanner .MultiValueSetting (confMultiValueGenre ),
160- scanner .AlbumArtist : scanner .MultiValueSetting (confMultiValueAlbumArtist ),
161- },
162- HTTPLog : * confHTTPLog ,
163- JukeboxEnabled : * confJukeboxEnabled ,
164- })
165- if err != nil {
166- log .Panicf ("error creating server: %v\n " , err )
167- }
168-
169158 log .Printf ("starting gonic v%s\n " , gonic .Version )
170159 log .Printf ("provided config\n " )
171160 set .VisitAll (func (f * flag.Flag ) {
172161 value := strings .ReplaceAll (f .Value .String (), "\n " , "" )
173162 log .Printf (" %-25s %s\n " , f .Name , value )
174163 })
175164
165+ tagger := & tags.TagReader {}
166+ scannr := scanner .New (
167+ ctrlsubsonic .PathsOf (musicPaths ),
168+ dbc ,
169+ map [scanner.Tag ]scanner.MultiValueSetting {
170+ scanner .Genre : scanner .MultiValueSetting (confMultiValueGenre ),
171+ scanner .AlbumArtist : scanner .MultiValueSetting (confMultiValueAlbumArtist ),
172+ },
173+ tagger ,
174+ * confExcludePatterns ,
175+ )
176+ podcast := podcasts .New (dbc , * confPodcastPath , tagger )
177+ transcoder := transcode .NewCachingTranscoder (
178+ transcode .NewFFmpegTranscoder (),
179+ cacheDirAudio ,
180+ )
181+ lastfmClient := lastfm .NewClient ()
182+ playlistStore , err := playlist .NewStore (* confPlaylistsPath )
183+ if err != nil {
184+ log .Panicf ("error creating playlists store: %v" , err )
185+ }
186+
187+ var jukebx * jukebox.Jukebox
188+ if * confJukeboxEnabled {
189+ jukebx = jukebox .New ()
190+ }
191+
192+ sessKey , err := dbc .GetSetting ("session_key" )
193+ if err != nil {
194+ log .Panicf ("error getting session key: %v\n " , err )
195+ }
196+ if sessKey == "" {
197+ if err := dbc .SetSetting ("session_key" , string (securecookie .GenerateRandomKey (32 ))); err != nil {
198+ log .Panicf ("error setting session key: %v\n " , err )
199+ }
200+ }
201+ sessDB := gormstore .New (dbc .DB , []byte (sessKey ))
202+ sessDB .SessionOpts .HttpOnly = true
203+ sessDB .SessionOpts .SameSite = http .SameSiteLaxMode
204+
205+ ctrlBase := & ctrlbase.Controller {
206+ DB : dbc ,
207+ PlaylistStore : playlistStore ,
208+ ProxyPrefix : * confProxyPrefix ,
209+ Scanner : scannr ,
210+ }
211+ ctrlAdmin , err := ctrladmin .New (ctrlBase , sessDB , podcast , lastfmClient )
212+ if err != nil {
213+ log .Panicf ("error creating admin controller: %v\n " , err )
214+ }
215+ ctrlSubsonic := & ctrlsubsonic.Controller {
216+ Controller : ctrlBase ,
217+ MusicPaths : musicPaths ,
218+ PodcastsPath : * confPodcastPath ,
219+ CacheAudioPath : cacheDirAudio ,
220+ CacheCoverPath : cacheDirCovers ,
221+ LastFMClient : lastfmClient ,
222+ Scrobblers : []scrobble.Scrobbler {
223+ lastfm .NewScrobbler (dbc , lastfmClient ),
224+ listenbrainz .NewScrobbler (),
225+ },
226+ Podcasts : podcast ,
227+ Transcoder : transcoder ,
228+ Jukebox : jukebx ,
229+ }
230+
231+ mux := mux .NewRouter ()
232+ ctrlbase .AddRoutes (ctrlBase , mux , * confHTTPLog )
233+ ctrladmin .AddRoutes (ctrlAdmin , mux .PathPrefix ("/admin" ).Subrouter ())
234+ ctrlsubsonic .AddRoutes (ctrlSubsonic , mux .PathPrefix ("/rest" ).Subrouter ())
235+
176236 var g run.Group
177- g .Add (server .StartHTTP (* confListenAddr , * confTLSCert , * confTLSKey ))
178- g .Add (server .StartSessionClean (cleanTimeDuration ))
179- g .Add (server .StartPodcastRefresher (time .Hour ))
237+ g .Add (func () error {
238+ log .Print ("starting job 'http'\n " )
239+ server := & http.Server {
240+ Addr : * confListenAddr ,
241+ Handler : mux ,
242+ ReadTimeout : 5 * time .Second ,
243+ ReadHeaderTimeout : 5 * time .Second ,
244+ WriteTimeout : 80 * time .Second ,
245+ IdleTimeout : 60 * time .Second ,
246+ }
247+ if * confTLSCert != "" && * confTLSKey != "" {
248+ return server .ListenAndServeTLS (* confTLSCert , * confTLSKey )
249+ }
250+ return server .ListenAndServe ()
251+ }, nil )
252+
253+ g .Add (func () error {
254+ log .Printf ("starting job 'session clean'\n " )
255+ ticker := time .NewTicker (10 * time .Minute )
256+ for range ticker .C {
257+ sessDB .Cleanup ()
258+ }
259+ return nil
260+ }, nil )
261+
262+ g .Add (func () error {
263+ log .Printf ("starting job 'podcast refresher'\n " )
264+ ticker := time .NewTicker (time .Hour )
265+ for range ticker .C {
266+ if err := podcast .RefreshPodcasts (); err != nil {
267+ log .Printf ("failed to refresh some feeds: %s" , err )
268+ }
269+ }
270+ return nil
271+ }, nil )
272+
273+ g .Add (func () error {
274+ log .Printf ("starting job 'podcast purger'\n " )
275+ ticker := time .NewTicker (24 * time .Hour )
276+ for range ticker .C {
277+ if err := podcast .PurgeOldPodcasts (time .Duration (* confPodcastPurgeAgeDays ) * 24 * time .Hour ); err != nil {
278+ log .Printf ("error purging old podcasts: %v" , err )
279+ }
280+ }
281+ return nil
282+ }, nil )
283+
180284 if * confScanIntervalMins > 0 {
181- tickerDur := time .Duration (* confScanIntervalMins ) * time .Minute
182- g .Add (server .StartScanTicker (tickerDur ))
285+ g .Add (func () error {
286+ log .Printf ("starting job 'scan timer'\n " )
287+ ticker := time .NewTicker (time .Duration (* confScanIntervalMins ) * time .Minute )
288+ for range ticker .C {
289+ if _ , err := scannr .ScanAndClean (scanner.ScanOptions {}); err != nil {
290+ log .Printf ("error scanning: %v" , err )
291+ }
292+ }
293+ return nil
294+ }, nil )
183295 }
296+
184297 if * confScanWatcher {
185- g .Add (server .StartScanWatcher ())
186- }
187- if * confJukeboxEnabled {
188- extraArgs , _ := shlex .Split (* confJukeboxMPVExtraArgs )
189- g .Add (server .StartJukebox (extraArgs ))
298+ g .Add (func () error {
299+ log .Printf ("starting job 'scan watcher'\n " )
300+ return scannr .ExecuteWatch ()
301+ }, func (_ error ) {
302+ scannr .CancelWatch ()
303+ })
190304 }
191- if * confPodcastPurgeAgeDays > 0 {
192- g .Add (server .StartPodcastPurger (time .Duration (* confPodcastPurgeAgeDays ) * 24 * time .Hour ))
305+
306+ if jukebx != nil {
307+ var jukeboxTempDir string
308+ g .Add (func () error {
309+ log .Printf ("starting job 'jukebox'\n " )
310+ extraArgs , _ := shlex .Split (* confJukeboxMPVExtraArgs )
311+ var err error
312+ jukeboxTempDir , err = os .MkdirTemp ("" , "gonic-jukebox-*" )
313+ if err != nil {
314+ return fmt .Errorf ("create tmp sock file: %w" , err )
315+ }
316+ sockPath := filepath .Join (jukeboxTempDir , "sock" )
317+ if err := jukebx .Start (sockPath , extraArgs ); err != nil {
318+ return fmt .Errorf ("start jukebox: %w" , err )
319+ }
320+ if err := jukebx .Wait (); err != nil {
321+ return fmt .Errorf ("start jukebox: %w" , err )
322+ }
323+ return nil
324+ }, func (_ error ) {
325+ if err := jukebx .Quit (); err != nil {
326+ log .Printf ("error quitting jukebox: %v" , err )
327+ }
328+ _ = os .RemoveAll (jukeboxTempDir )
329+ })
193330 }
331+
194332 if * confScanAtStart {
195- server .ScanAtStart ()
333+ if _ , err := scannr .ScanAndClean (scanner.ScanOptions {}); err != nil {
334+ log .Panicf ("error scanning at start: %v\n " , err )
335+ }
196336 }
197337
198338 if err := g .Run (); err != nil {
0 commit comments