Skip to content

Commit

Permalink
feat: add multi folder support
Browse files Browse the repository at this point in the history
closes #50
  • Loading branch information
sentriz committed Nov 6, 2021
1 parent fa587fc commit 40cd031
Show file tree
Hide file tree
Showing 32 changed files with 673 additions and 535 deletions.
38 changes: 32 additions & 6 deletions cmd/gonic/gonic.go
Expand Up @@ -30,7 +30,6 @@ const (
func main() {
set := flag.NewFlagSet(gonic.Name, flag.ExitOnError)
confListenAddr := set.String("listen-addr", "0.0.0.0:4747", "listen address (optional)")
confMusicPath := set.String("music-path", "", "path to music")
confPodcastPath := set.String("podcast-path", "", "path to podcasts")
confCachePath := set.String("cache-path", "", "path to cache")
confDBPath := set.String("db-path", "gonic.db", "path to database (optional)")
Expand All @@ -40,6 +39,10 @@ func main() {
confGenreSplit := set.String("genre-split", "\n", "character or string to split genre tag data on (optional)")
confHTTPLog := set.Bool("http-log", true, "http request logging (optional)")
confShowVersion := set.Bool("version", false, "show gonic version")

var confMusicPaths musicPaths
set.Var(&confMusicPaths, "music-path", "path to music")

_ = set.String("config-path", "", "path to config (optional)")

if err := ff.Parse(set, os.Args[1:],
Expand All @@ -62,8 +65,13 @@ func main() {
log.Printf(" %-15s %s\n", f.Name, value)
})

if _, err := os.Stat(*confMusicPath); os.IsNotExist(err) {
log.Fatal("please provide a valid music directory")
if len(confMusicPaths) == 0 {
log.Fatalf("please provide a music directory")
}
for _, confMusicPath := range confMusicPaths {
if _, err := os.Stat(confMusicPath); os.IsNotExist(err) {
log.Fatalf("music directory %q not found", confMusicPath)
}
}
if _, err := os.Stat(*confPodcastPath); os.IsNotExist(err) {
log.Fatal("please provide a valid podcast directory")
Expand All @@ -90,13 +98,20 @@ func main() {
if err != nil {
log.Fatalf("error opening database: %v\n", err)
}
defer db.Close()
defer dbc.Close()

err = dbc.Migrate(db.MigrationContext{
OriginalMusicPath: confMusicPaths[0],
})
if err != nil {
log.Panicf("error migrating database: %v\n", err)
}

proxyPrefixExpr := regexp.MustCompile(`^\/*(.*?)\/*$`)
*confProxyPrefix = proxyPrefixExpr.ReplaceAllString(*confProxyPrefix, `/$1`)
server, err := server.New(server.Options{
DB: db,
MusicPath: *confMusicPath,
DB: dbc,
MusicPaths: confMusicPaths,
CachePath: cacheDirAudio,
CoverCachePath: cacheDirCovers,
ProxyPrefix: *confProxyPrefix,
Expand Down Expand Up @@ -125,3 +140,14 @@ func main() {
log.Panicf("error in job: %v", err)
}
}

type musicPaths []string

func (m musicPaths) String() string {
return strings.Join(m, ", ")
}

func (m *musicPaths) Set(value string) error {
*m = append(*m, value)
return nil
}
70 changes: 14 additions & 56 deletions server/ctrladmin/handlers.go
Expand Up @@ -139,10 +139,7 @@ func (c *Controller) ServeChangeOwnPasswordDo(r *http.Request) *Response {
func (c *Controller) ServeLinkLastFMDo(r *http.Request) *Response {
token := r.URL.Query().Get("token")
if token == "" {
return &Response{
err: "please provide a token",
code: 400,
}
return &Response{code: 400, err: "please provide a token"}
}
apiKey, err := c.DB.GetSetting("lastfm_api_key")
if err != nil {
Expand Down Expand Up @@ -199,17 +196,11 @@ func (c *Controller) ServeUnlinkListenBrainzDo(r *http.Request) *Response {
func (c *Controller) ServeChangeUsername(r *http.Request) *Response {
username := r.URL.Query().Get("user")
if username == "" {
return &Response{
err: "please provide a username",
code: 400,
}
return &Response{code: 400, err: "please provide a username"}
}
user := c.DB.GetUserByName(username)
if user == nil {
return &Response{
err: "couldn't find a user with that name",
code: 400,
}
return &Response{code: 400, err: "couldn't find a user with that name"}
}
data := &templateData{}
data.SelectedUser = user
Expand Down Expand Up @@ -237,17 +228,11 @@ func (c *Controller) ServeChangeUsernameDo(r *http.Request) *Response {
func (c *Controller) ServeChangePassword(r *http.Request) *Response {
username := r.URL.Query().Get("user")
if username == "" {
return &Response{
err: "please provide a username",
code: 400,
}
return &Response{code: 400, err: "please provide a username"}
}
user := c.DB.GetUserByName(username)
if user == nil {
return &Response{
err: "couldn't find a user with that name",
code: 400,
}
return &Response{code: 400, err: "couldn't find a user with that name"}
}
data := &templateData{}
data.SelectedUser = user
Expand Down Expand Up @@ -276,17 +261,11 @@ func (c *Controller) ServeChangePasswordDo(r *http.Request) *Response {
func (c *Controller) ServeDeleteUser(r *http.Request) *Response {
username := r.URL.Query().Get("user")
if username == "" {
return &Response{
err: "please provide a username",
code: 400,
}
return &Response{code: 400, err: "please provide a username"}
}
user := c.DB.GetUserByName(username)
if user == nil {
return &Response{
err: "couldn't find a user with that name",
code: 400,
}
return &Response{code: 400, err: "couldn't find a user with that name"}
}
data := &templateData{}
data.SelectedUser = user
Expand Down Expand Up @@ -421,10 +400,7 @@ func (c *Controller) ServeDeleteTranscodePrefDo(r *http.Request) *Response {
user := r.Context().Value(CtxUser).(*db.User)
client := r.URL.Query().Get("client")
if client == "" {
return &Response{
err: "please provide a client",
code: 400,
}
return &Response{code: 400, err: "please provide a client"}
}
c.DB.
Where("user_id=? AND client=?", user.ID, client).
Expand Down Expand Up @@ -459,16 +435,10 @@ func (c *Controller) ServePodcastAddDo(r *http.Request) *Response {
func (c *Controller) ServePodcastDownloadDo(r *http.Request) *Response {
id, err := strconv.Atoi(r.URL.Query().Get("id"))
if err != nil {
return &Response{
err: "please provide a valid podcast id",
code: 400,
}
return &Response{code: 400, err: "please provide a valid podcast id"}
}
if err := c.Podcasts.DownloadPodcastAll(id); err != nil {
return &Response{
err: "please provide a valid podcast id",
code: 400,
}
return &Response{code: 400, err: "please provide a valid podcast id"}
}
return &Response{
redirect: "/admin/home",
Expand All @@ -479,10 +449,7 @@ func (c *Controller) ServePodcastDownloadDo(r *http.Request) *Response {
func (c *Controller) ServePodcastUpdateDo(r *http.Request) *Response {
id, err := strconv.Atoi(r.URL.Query().Get("id"))
if err != nil {
return &Response{
err: "please provide a valid podcast id",
code: 400,
}
return &Response{code: 400, err: "please provide a valid podcast id"}
}
setting := db.PodcastAutoDownload(r.FormValue("setting"))
var message string
Expand All @@ -492,10 +459,7 @@ func (c *Controller) ServePodcastUpdateDo(r *http.Request) *Response {
case db.PodcastAutoDownloadNone:
message = "future podcast episodes will not be downloaded"
default:
return &Response{
err: "please provide a valid podcast download type",
code: 400,
}
return &Response{code: 400, err: "please provide a valid podcast download type"}
}
if err := c.Podcasts.SetAutoDownload(id, setting); err != nil {
return &Response{
Expand All @@ -513,16 +477,10 @@ func (c *Controller) ServePodcastDeleteDo(r *http.Request) *Response {
user := r.Context().Value(CtxUser).(*db.User)
id, err := strconv.Atoi(r.URL.Query().Get("id"))
if err != nil {
return &Response{
err: "please provide a valid podcast id",
code: 400,
}
return &Response{code: 400, err: "please provide a valid podcast id"}
}
if err := c.Podcasts.DeletePodcast(user.ID, id); err != nil {
return &Response{
err: "please provide a valid podcast id",
code: 400,
}
return &Response{code: 400, err: "please provide a valid podcast id"}
}
return &Response{
redirect: "/admin/home",
Expand Down
18 changes: 6 additions & 12 deletions server/ctrladmin/handlers_playlist.go
Expand Up @@ -18,16 +18,16 @@ var (
errPlaylistNoMatch = errors.New("couldn't match track")
)

func playlistParseLine(c *Controller, path string) (int, error) {
if strings.HasPrefix(path, "#") || strings.TrimSpace(path) == "" {
func playlistParseLine(c *Controller, absPath string) (int, error) {
if strings.HasPrefix(absPath, "#") || strings.TrimSpace(absPath) == "" {
return 0, nil
}
var track db.Track
query := c.DB.Raw(`
SELECT tracks.id FROM TRACKS
JOIN albums ON tracks.album_id=albums.id
WHERE (? || '/' || albums.left_path || albums.right_path || '/' || tracks.filename)=?`,
c.MusicPath, path)
WHERE (albums.root_dir || '/' || albums.left_path || albums.right_path || '/' || tracks.filename)=?`,
absPath)
err := query.First(&track).Error
switch {
case errors.Is(err, gorm.ErrRecordNotFound):
Expand Down Expand Up @@ -95,10 +95,7 @@ func (c *Controller) ServeUploadPlaylist(r *http.Request) *Response {

func (c *Controller) ServeUploadPlaylistDo(r *http.Request) *Response {
if err := r.ParseMultipartForm((1 << 10) * 24); err != nil {
return &Response{
err: "couldn't parse mutlipart",
code: 500,
}
return &Response{code: 500, err: "couldn't parse mutlipart"}
}
user := r.Context().Value(CtxUser).(*db.User)
var playlistCount int
Expand All @@ -123,10 +120,7 @@ func (c *Controller) ServeDeletePlaylistDo(r *http.Request) *Response {
user := r.Context().Value(CtxUser).(*db.User)
id, err := strconv.Atoi(r.URL.Query().Get("id"))
if err != nil {
return &Response{
err: "please provide a valid id",
code: 400,
}
return &Response{code: 400, err: "please provide a valid id"}
}
c.DB.
Where("user_id=? AND id=?", user.ID, id).
Expand Down
1 change: 0 additions & 1 deletion server/ctrlbase/ctrl.go
Expand Up @@ -46,7 +46,6 @@ func statusToBlock(code int) string {

type Controller struct {
DB *db.DB
MusicPath string
Scanner *scanner.Scanner
ProxyPrefix string
}
Expand Down
10 changes: 4 additions & 6 deletions server/ctrlsubsonic/ctrl_test.go
Expand Up @@ -55,15 +55,14 @@ func makeHTTPMock(query url.Values) (*httptest.ResponseRecorder, *http.Request)
func runQueryCases(t *testing.T, contr *Controller, h handlerSubsonic, cases []*queryCase) {
t.Helper()
for _, qc := range cases {
qc := qc // pin
t.Run(qc.expectPath, func(t *testing.T) {
t.Parallel()
rr, req := makeHTTPMock(qc.params)
contr.H(h).ServeHTTP(rr, req)
body := rr.Body.String()
if status := rr.Code; status != http.StatusOK {
t.Fatalf("didn't give a 200\n%s", body)
}

goldenPath := makeGoldenPath(t.Name())
goldenRegen := os.Getenv("GONIC_REGEN")
if goldenRegen == "*" || (goldenRegen != "" && strings.HasPrefix(t.Name(), goldenRegen)) {
Expand All @@ -86,11 +85,10 @@ func runQueryCases(t *testing.T, contr *Controller, h handlerSubsonic, cases []*
diffOpts = append(diffOpts, jd.SET)
}
diff := expected.Diff(actual, diffOpts...)
// pass or fail
if len(diff) == 0 {
return

if len(diff) > 0 {
t.Errorf("\u001b[31;1mdiffering json\u001b[0m\n%s", diff.Render())
}
t.Errorf("\u001b[31;1mdiffering json\u001b[0m\n%s", diff.Render())
})
}
}
Expand Down

0 comments on commit 40cd031

Please sign in to comment.