From 1647eaac4585cca7a244036f9c242a5602706b83 Mon Sep 17 00:00:00 2001 From: Gonzalo Arreche Date: Thu, 24 Feb 2022 01:48:31 -0300 Subject: [PATCH] feat(subsonic): support public playlists When multiple people share the same instance, they might want to share their playlists between them. This allows people to mark playlists as public, and to listen to public playlists from other people. Listeners will also know who owns the playlist, to help avoid confusion and make this feature a bit nicer. Subsonic restrict updating playlists only to owners, this honors that behavior, but adding flexibility could be achieved easily. --- server/ctrlsubsonic/handlers_playlist.go | 13 ++++++++++++- server/db/migrations.go | 5 +++++ server/db/model.go | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/server/ctrlsubsonic/handlers_playlist.go b/server/ctrlsubsonic/handlers_playlist.go index 7cdec2f0..ba5fefb9 100644 --- a/server/ctrlsubsonic/handlers_playlist.go +++ b/server/ctrlsubsonic/handlers_playlist.go @@ -23,6 +23,8 @@ func playlistRender(c *Controller, playlist *db.Playlist) *spec.Playlist { Comment: playlist.Comment, Created: playlist.CreatedAt, SongCount: playlist.TrackCount, + Public: playlist.IsPublic, + Owner: user.Name, } trackIDs := playlist.GetItems() @@ -48,7 +50,7 @@ func playlistRender(c *Controller, playlist *db.Playlist) *spec.Playlist { func (c *Controller) ServeGetPlaylists(r *http.Request) *spec.Response { user := r.Context().Value(CtxUser).(*db.User) var playlists []*db.Playlist - c.DB.Where("user_id=?", user.ID).Find(&playlists) + c.DB.Where("user_id=?", user.ID).Or("is_public=?", true).Find(&playlists) sub := spec.NewResponse() sub.Playlists = &spec.Playlists{ List: make([]*spec.Playlist, len(playlists)), @@ -90,6 +92,9 @@ func (c *Controller) ServeCreatePlaylist(r *http.Request) *spec.Response { FirstOrCreate(&playlist) // update meta info + if playlist.UserID != 0 && playlist.UserID != user.ID { + return spec.NewResponse() + } playlist.UserID = user.ID if val, err := params.Get("name"); err == nil { playlist.Name = val @@ -123,6 +128,9 @@ func (c *Controller) ServeUpdatePlaylist(r *http.Request) *spec.Response { FirstOrCreate(&playlist) // update meta info + if playlist.UserID != 0 && playlist.UserID != user.ID { + return spec.NewResponse() + } playlist.UserID = user.ID if val, err := params.Get("name"); err == nil { playlist.Name = val @@ -130,6 +138,9 @@ func (c *Controller) ServeUpdatePlaylist(r *http.Request) *spec.Response { if val, err := params.Get("comment"); err == nil { playlist.Comment = val } + if val, err := params.GetBool("public"); err == nil { + playlist.IsPublic = val + } trackIDs := playlist.GetItems() // delete items diff --git a/server/db/migrations.go b/server/db/migrations.go index 9e6219ef..6434bec6 100644 --- a/server/db/migrations.go +++ b/server/db/migrations.go @@ -40,6 +40,7 @@ func (db *DB) Migrate(ctx MigrationContext) error { construct(ctx, "202201042236", migrateArtistGuessedFolder), construct(ctx, "202202092013", migrateArtistCover), construct(ctx, "202202121809", migrateAlbumRootDirAgain), + construct(ctx, "202202241218", migratePublicPlaylist), } return gormigrate. @@ -327,3 +328,7 @@ func migrateArtistCover(tx *gorm.DB, ctx MigrationContext) error { func migrateAlbumRootDirAgain(tx *gorm.DB, ctx MigrationContext) error { return migrateAlbumRootDir(tx, ctx) } + +func migratePublicPlaylist(tx *gorm.DB, ctx MigrationContext) error { + return tx.AutoMigrate(Playlist{}).Error +} diff --git a/server/db/model.go b/server/db/model.go index d9d0d6bb..814b5e49 100644 --- a/server/db/model.go +++ b/server/db/model.go @@ -247,6 +247,7 @@ type Playlist struct { Comment string TrackCount int Items string + IsPublic bool `sql:"default: null"` } func (p *Playlist) GetItems() []int {