1
1
// Package main is the gonic server entrypoint
2
2
//
3
- //nolint:lll // flags help strings
3
+ //nolint:lll,gocyclo
4
4
package main
5
5
6
6
import (
7
7
"errors"
8
8
"flag"
9
9
"fmt"
10
10
"log"
11
+ "net/http"
11
12
"os"
12
13
"path"
13
14
"path/filepath"
@@ -16,19 +17,27 @@ import (
16
17
"time"
17
18
18
19
"github.com/google/shlex"
20
+ "github.com/gorilla/mux"
21
+ "github.com/gorilla/securecookie"
19
22
_ "github.com/jinzhu/gorm/dialects/sqlite"
20
23
"github.com/oklog/run"
21
24
"github.com/peterbourgon/ff"
25
+ "github.com/sentriz/gormstore"
22
26
23
27
"go.senan.xyz/gonic"
24
28
"go.senan.xyz/gonic/db"
29
+ "go.senan.xyz/gonic/jukebox"
30
+ "go.senan.xyz/gonic/playlist"
31
+ "go.senan.xyz/gonic/podcasts"
25
32
"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"
27
39
"go.senan.xyz/gonic/server/ctrlsubsonic"
28
- )
29
-
30
- const (
31
- cleanTimeDuration = 10 * time .Minute
40
+ "go.senan.xyz/gonic/transcode"
32
41
)
33
42
34
43
func main () {
@@ -146,53 +155,184 @@ func main() {
146
155
* deprecatedConfGenreSplit = "<deprecated>"
147
156
}
148
157
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
-
169
158
log .Printf ("starting gonic v%s\n " , gonic .Version )
170
159
log .Printf ("provided config\n " )
171
160
set .VisitAll (func (f * flag.Flag ) {
172
161
value := strings .ReplaceAll (f .Value .String (), "\n " , "" )
173
162
log .Printf (" %-25s %s\n " , f .Name , value )
174
163
})
175
164
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
+
176
236
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
+
180
284
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 )
183
295
}
296
+
184
297
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
+ })
190
304
}
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
+ })
193
330
}
331
+
194
332
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
+ }
196
336
}
197
337
198
338
if err := g .Run (); err != nil {
0 commit comments