add support for subsonic podcast api

This commit is contained in:
Alex McGrath
2021-02-03 20:38:01 +00:00
committed by Senan Kelly
parent ce96b9f6fa
commit 9c4286b0e2
21 changed files with 2011 additions and 1000 deletions

View File

@@ -17,6 +17,7 @@ import (
"go.senan.xyz/gonic/server/ctrlsubsonic"
"go.senan.xyz/gonic/server/db"
"go.senan.xyz/gonic/server/jukebox"
"go.senan.xyz/gonic/server/podcasts"
"go.senan.xyz/gonic/server/scanner"
"go.senan.xyz/gonic/server/scrobble"
"go.senan.xyz/gonic/server/scrobble/lastfm"
@@ -26,6 +27,7 @@ import (
type Options struct {
DB *db.DB
MusicPath string
PodcastPath string
CachePath string
CoverCachePath string
ProxyPrefix string
@@ -37,12 +39,14 @@ type Server struct {
jukebox *jukebox.Jukebox
router *mux.Router
sessDB *gormstore.Store
podcast *podcasts.Podcasts
}
func New(opts Options) *Server {
// ** begin sanitation
opts.MusicPath = filepath.Clean(opts.MusicPath)
opts.CachePath = filepath.Clean(opts.CachePath)
opts.PodcastPath = filepath.Clean(opts.PodcastPath)
// ** begin controllers
scanner := scanner.New(opts.MusicPath, opts.DB, opts.GenreSplit)
jukebox := jukebox.New(opts.MusicPath)
@@ -64,7 +68,8 @@ func New(opts Options) *Server {
sessDB.SessionOpts.HttpOnly = true
sessDB.SessionOpts.SameSite = http.SameSiteLaxMode
//
ctrlAdmin := ctrladmin.New(base, sessDB)
pcInit := &podcasts.Podcasts{DB: opts.DB, PodcastBasePath: opts.PodcastPath}
ctrlAdmin := ctrladmin.New(base, sessDB, pcInit)
scrobblers := []scrobble.Scrobbler{
&lastfm.Scrobbler{DB: opts.DB},
&listenbrainz.Scrobbler{},
@@ -75,6 +80,7 @@ func New(opts Options) *Server {
CoverCachePath: opts.CoverCachePath,
Jukebox: jukebox,
Scrobblers: scrobblers,
Podcasts: pcInit,
}
setupMisc(r, base)
setupAdmin(r.PathPrefix("/admin").Subrouter(), ctrlAdmin)
@@ -85,6 +91,7 @@ func New(opts Options) *Server {
jukebox: jukebox,
router: r,
sessDB: sessDB,
podcast: &podcasts.Podcasts{DB: opts.DB, PodcastBasePath: opts.PodcastPath},
}
}
@@ -135,6 +142,10 @@ func setupAdmin(r *mux.Router, ctrl *ctrladmin.Controller) {
routUser.Handle("/delete_playlist_do", ctrl.H(ctrl.ServeDeletePlaylistDo))
routUser.Handle("/create_transcode_pref_do", ctrl.H(ctrl.ServeCreateTranscodePrefDo))
routUser.Handle("/delete_transcode_pref_do", ctrl.H(ctrl.ServeDeleteTranscodePrefDo))
if ctrl.Podcasts.PodcastBasePath != "" {
routUser.Handle("/add_podcast_do", ctrl.H(ctrl.ServePodcastAddDo))
routUser.Handle("/delete_podcast_do", ctrl.H(ctrl.ServePodcastDeleteDo))
}
// ** begin admin routes (if session is valid, and is admin)
routAdmin := routUser.NewRoute().Subrouter()
routAdmin.Use(ctrl.WithAdminSession)
@@ -198,8 +209,15 @@ func setupSubsonic(r *mux.Router, ctrl *ctrlsubsonic.Controller) {
r.Handle("/search2{_:(?:\\.view)?}", ctrl.H(ctrl.ServeSearchTwo))
r.Handle("/getGenres{_:(?:\\.view)?}", ctrl.H(ctrl.ServeGetGenres))
r.Handle("/getArtistInfo{_:(?:\\.view)?}", ctrl.H(ctrl.ServeGetArtistInfo))
// ** begin unimplemented
r.Handle("/getPodcasts{_:(?:\\.view)?}", ctrl.H(ctrl.ServeGetPodcasts))
// ** begin podcasts
if ctrl.Podcasts.PodcastBasePath != "" {
r.Handle("/getPodcasts{_:(?:\\.view)?}", ctrl.H(ctrl.ServeGetPodcasts))
r.Handle("/downloadPodcastEpisode{_:(?:\\.view)?}", ctrl.H(ctrl.ServeDownloadPodcastEpisode))
r.Handle("/createPodcastChannel{_:(?:\\.view)?}", ctrl.H(ctrl.ServeCreatePodcastChannel))
r.Handle("/refreshPodcasts{_:(?:\\.view)?}", ctrl.H(ctrl.ServeRefreshPodcasts))
r.Handle("/deletePodcastChannel{_:(?:\\.view)?}", ctrl.H(ctrl.ServeDeletePodcastChannel))
r.Handle("/deletePodcastEpisode{_:(?:\\.view)?}", ctrl.H(ctrl.ServeDeletePodcastEpisode))
}
// middlewares should be run for not found handler
// https://github.com/gorilla/mux/issues/416
notFoundHandler := ctrl.H(ctrl.ServeNotFound)
@@ -264,6 +282,31 @@ func (s *Server) StartJukebox() (FuncExecute, FuncInterrupt) {
}
}
func (s *Server) StartPodcastRefresher(dur time.Duration) (FuncExecute, FuncInterrupt) {
ticker := time.NewTicker(dur)
done := make(chan struct{})
waitFor := func() error {
for {
select {
case <-done:
return nil
case <-ticker.C:
if err := s.podcast.RefreshPodcasts(0, true); err != nil {
log.Printf("failed to refresh some feeds: %s", err)
}
}
}
}
return func() error {
log.Printf("starting job 'podcast refresher'\n")
return waitFor()
}, func(_ error) {
// stop job
ticker.Stop()
done <- struct{}{}
}
}
func (s *Server) StartSessionClean(dur time.Duration) (FuncExecute, FuncInterrupt) {
ticker := time.NewTicker(dur)
done := make(chan struct{})