refactor(podcast)!: make podcasts global not per user, to match spec
Release-As: 0.15.0
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,3 +15,4 @@ gonicscan
|
||||
gonicembed
|
||||
.vscode
|
||||
*.swp
|
||||
.tags*
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
- browsing by tags (using [taglib](https://taglib.org/) - supports mp3, opus, flac, ape, m4a, wav, etc.)
|
||||
- on-the-fly audio transcoding and caching (requires [ffmpeg](https://ffmpeg.org/)) (thank you [spijet](https://github.com/spijet/))
|
||||
- jukebox mode (thank you [lxea](https://github.com/lxea/))
|
||||
- support for per-user podcasts (thank you [lxea](https://github.com/lxea/))
|
||||
- support for podcasts (thank you [lxea](https://github.com/lxea/))
|
||||
- pretty fast scanning (with my library of ~27k tracks, initial scan takes about 10m, and about 5s after incrementally)
|
||||
- multiple users, each with their own transcoding preferences, playlists, top tracks, top artists, etc.
|
||||
- [last.fm](https://www.last.fm/) scrobbling
|
||||
|
||||
@@ -41,6 +41,7 @@ func (db *DB) Migrate(ctx MigrationContext) error {
|
||||
construct(ctx, "202202092013", migrateArtistCover),
|
||||
construct(ctx, "202202121809", migrateAlbumRootDirAgain),
|
||||
construct(ctx, "202202241218", migratePublicPlaylist),
|
||||
construct(ctx, "202204270903", migratePodcastDropUserID),
|
||||
}
|
||||
|
||||
return gormigrate.
|
||||
@@ -332,3 +333,26 @@ func migrateAlbumRootDirAgain(tx *gorm.DB, ctx MigrationContext) error {
|
||||
func migratePublicPlaylist(tx *gorm.DB, ctx MigrationContext) error {
|
||||
return tx.AutoMigrate(Playlist{}).Error
|
||||
}
|
||||
|
||||
func migratePodcastDropUserID(tx *gorm.DB, _ MigrationContext) error {
|
||||
step := tx.AutoMigrate(
|
||||
Podcast{},
|
||||
)
|
||||
if err := step.Error; err != nil {
|
||||
return fmt.Errorf("step auto migrate: %w", err)
|
||||
}
|
||||
|
||||
if !tx.Dialect().HasColumn("podcasts", "user_id") {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
step = tx.Exec(`
|
||||
ALTER TABLE podcasts DROP COLUMN user_id;
|
||||
`)
|
||||
if err := step.Error; err != nil {
|
||||
return fmt.Errorf("step migrate podcasts drop user_id: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -7,15 +7,14 @@ package db
|
||||
|
||||
import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
// TODO: remove this dep
|
||||
|
||||
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
|
||||
"go.senan.xyz/gonic/mime"
|
||||
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
|
||||
)
|
||||
|
||||
func splitInt(in, sep string) []int {
|
||||
@@ -315,7 +314,6 @@ type Podcast struct {
|
||||
ID int `gorm:"primary_key"`
|
||||
UpdatedAt time.Time
|
||||
ModifiedAt time.Time
|
||||
UserID int `sql:"default: null; type:int REFERENCES users(id) ON DELETE CASCADE"`
|
||||
URL string
|
||||
Title string
|
||||
Description string
|
||||
@@ -326,11 +324,6 @@ type Podcast struct {
|
||||
AutoDownload PodcastAutoDownload
|
||||
}
|
||||
|
||||
func (p *Podcast) Fullpath(podcastPath string) string {
|
||||
sanitizedTitle := strings.ReplaceAll(p.Title, "/", "_")
|
||||
return filepath.Join(podcastPath, filepath.Clean(sanitizedTitle))
|
||||
}
|
||||
|
||||
func (p *Podcast) SID() *specid.ID {
|
||||
return &specid.ID{Type: specid.Podcast, Value: p.ID}
|
||||
}
|
||||
|
||||
@@ -40,13 +40,15 @@ func New(db *db.DB, base string, tagger tags.Reader) *Podcasts {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Podcasts) GetPodcastOrAll(userID int, id int, includeEpisodes bool) ([]*db.Podcast, error) {
|
||||
func (p *Podcasts) GetPodcastOrAll(id int, includeEpisodes bool) ([]*db.Podcast, error) {
|
||||
var err error
|
||||
podcasts := []*db.Podcast{}
|
||||
q := p.db.Where("user_id=?", userID)
|
||||
if id != 0 {
|
||||
q = q.Where("id=?", id)
|
||||
err = p.db.Where("id=?", id).Find(&podcasts).Error
|
||||
} else {
|
||||
err = p.db.Find(&podcasts).Error
|
||||
}
|
||||
if err := q.Find(&podcasts).Error; err != nil {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("finding podcasts: %w", err)
|
||||
}
|
||||
if !includeEpisodes {
|
||||
@@ -88,16 +90,14 @@ func (p *Podcasts) GetNewestPodcastEpisodes(count int) ([]*db.PodcastEpisode, er
|
||||
return episodes, nil
|
||||
}
|
||||
|
||||
func (p *Podcasts) AddNewPodcast(rssURL string, feed *gofeed.Feed,
|
||||
userID int) (*db.Podcast, error) {
|
||||
func (p *Podcasts) AddNewPodcast(rssURL string, feed *gofeed.Feed) (*db.Podcast, error) {
|
||||
podcast := db.Podcast{
|
||||
Description: feed.Description,
|
||||
ImageURL: feed.Image.URL,
|
||||
UserID: userID,
|
||||
Title: feed.Title,
|
||||
URL: rssURL,
|
||||
}
|
||||
podPath := podcast.Fullpath(p.baseDir)
|
||||
podPath := absPath(p.baseDir, &podcast)
|
||||
err := os.Mkdir(podPath, 0755)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
@@ -252,8 +252,7 @@ func itemToEpisode(podcastID, size, duration int, audio string,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Podcasts) findEnclosureAudio(podcastID, duration int,
|
||||
item *gofeed.Item) (*db.PodcastEpisode, bool) {
|
||||
func (p *Podcasts) findEnclosureAudio(podcastID, duration int, item *gofeed.Item) (*db.PodcastEpisode, bool) {
|
||||
for _, enc := range item.Enclosures {
|
||||
if !isAudio(enc.Type, enc.URL) {
|
||||
continue
|
||||
@@ -264,8 +263,7 @@ func (p *Podcasts) findEnclosureAudio(podcastID, duration int,
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (p *Podcasts) findMediaAudio(podcastID, duration int,
|
||||
item *gofeed.Item) (*db.PodcastEpisode, bool) {
|
||||
func (p *Podcasts) findMediaAudio(podcastID, duration int, item *gofeed.Item) (*db.PodcastEpisode, bool) {
|
||||
extensions, ok := item.Extensions["media"]["content"]
|
||||
if !ok {
|
||||
return nil, false
|
||||
@@ -291,22 +289,6 @@ func (p *Podcasts) RefreshPodcasts() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Podcasts) RefreshPodcastsForUser(userID int) error {
|
||||
podcasts := []*db.Podcast{}
|
||||
err := p.db.
|
||||
Where("user_id=?", userID).
|
||||
Find(&podcasts).
|
||||
Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("find podcasts: %w", err)
|
||||
}
|
||||
var errs *multierr.Err
|
||||
if errors.As(p.refreshPodcasts(podcasts), &errs) && errs.Len() > 0 {
|
||||
return fmt.Errorf("refresh podcasts: %w", errs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Podcasts) refreshPodcasts(podcasts []*db.Podcast) error {
|
||||
errs := &multierr.Err{}
|
||||
for _, podcast := range podcasts {
|
||||
@@ -387,13 +369,12 @@ func (p *Podcasts) DownloadEpisode(episodeID int) error {
|
||||
filename = path.Base(audioURL.Path)
|
||||
}
|
||||
filename = p.findUniqueEpisodeName(&podcast, &podcastEpisode, filename)
|
||||
audioFile, err := os.Create(path.Join(podcast.Fullpath(p.baseDir), filename))
|
||||
audioFile, err := os.Create(path.Join(absPath(p.baseDir, &podcast), filename))
|
||||
if err != nil {
|
||||
return fmt.Errorf("create audio file: %w", err)
|
||||
}
|
||||
podcastEpisode.Filename = filename
|
||||
sanTitle := strings.ReplaceAll(podcast.Title, "/", "_")
|
||||
podcastEpisode.Path = path.Join(sanTitle, filename)
|
||||
podcastEpisode.Path = path.Join(pathSafe(podcast.Title), filename)
|
||||
p.db.Save(&podcastEpisode)
|
||||
go func() {
|
||||
if err := p.doPodcastDownload(&podcastEpisode, audioFile, resp.Body); err != nil {
|
||||
@@ -403,22 +384,18 @@ func (p *Podcasts) DownloadEpisode(episodeID int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Podcasts) findUniqueEpisodeName(
|
||||
podcast *db.Podcast,
|
||||
podcastEpisode *db.PodcastEpisode,
|
||||
filename string) string {
|
||||
podcastPath := path.Join(podcast.Fullpath(p.baseDir), filename)
|
||||
func (p *Podcasts) findUniqueEpisodeName(podcast *db.Podcast, podcastEpisode *db.PodcastEpisode, filename string) string {
|
||||
podcastPath := path.Join(absPath(p.baseDir, podcast), filename)
|
||||
if _, err := os.Stat(podcastPath); os.IsNotExist(err) {
|
||||
return filename
|
||||
}
|
||||
sanitizedTitle := strings.ReplaceAll(podcastEpisode.Title, "/", "_")
|
||||
titlePath := fmt.Sprintf("%s%s", sanitizedTitle, filepath.Ext(filename))
|
||||
podcastPath = path.Join(podcast.Fullpath(p.baseDir), titlePath)
|
||||
titlePath := fmt.Sprintf("%s%s", pathSafe(podcastEpisode.Title), filepath.Ext(filename))
|
||||
podcastPath = path.Join(absPath(p.baseDir, podcast), titlePath)
|
||||
if _, err := os.Stat(podcastPath); os.IsNotExist(err) {
|
||||
return titlePath
|
||||
}
|
||||
// try to find a filename like FILENAME (1).mp3 incrementing
|
||||
return findEpisode(podcast.Fullpath(p.baseDir), filename, 1)
|
||||
return findEpisode(absPath(p.baseDir, podcast), filename, 1)
|
||||
}
|
||||
|
||||
func findEpisode(base, filename string, count int) string {
|
||||
@@ -462,9 +439,7 @@ func (p *Podcasts) downloadPodcastCover(podPath string, podcast *db.Podcast) err
|
||||
if _, err := io.Copy(coverFile, resp.Body); err != nil {
|
||||
return fmt.Errorf("writing podcast cover: %w", err)
|
||||
}
|
||||
podcastPath := filepath.Clean(strings.ReplaceAll(podcast.Title, "/", "_"))
|
||||
podcastFilename := fmt.Sprintf("cover%s", ext)
|
||||
podcast.ImagePath = path.Join(podcastPath, podcastFilename)
|
||||
podcast.ImagePath = path.Join(pathSafe(podcast.Title), fmt.Sprintf("cover%s", ext))
|
||||
if err := p.db.Save(podcast).Error; err != nil {
|
||||
return fmt.Errorf("save podcast: %w", err)
|
||||
}
|
||||
@@ -492,28 +467,20 @@ func (p *Podcasts) doPodcastDownload(podcastEpisode *db.PodcastEpisode, file *os
|
||||
return p.db.Save(podcastEpisode).Error
|
||||
}
|
||||
|
||||
func (p *Podcasts) DeletePodcast(userID, podcastID int) error {
|
||||
func (p *Podcasts) DeletePodcast(podcastID int) error {
|
||||
podcast := db.Podcast{}
|
||||
err := p.db.
|
||||
Where("id=? AND user_id=?", podcastID, userID).
|
||||
Where("id=?", podcastID).
|
||||
First(&podcast).
|
||||
Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var userCount int
|
||||
p.db.
|
||||
Model(&db.Podcast{}).
|
||||
Where("title=?", podcast.Title).
|
||||
Count(&userCount)
|
||||
if userCount == 1 {
|
||||
// only delete the folder if there are not multiple listeners
|
||||
if err = os.RemoveAll(podcast.Fullpath(p.baseDir)); err != nil {
|
||||
return fmt.Errorf("delete podcast directory: %w", err)
|
||||
}
|
||||
if err := os.RemoveAll(absPath(p.baseDir, &podcast)); err != nil {
|
||||
return fmt.Errorf("delete podcast directory: %w", err)
|
||||
}
|
||||
err = p.db.
|
||||
Where("id=? AND user_id=?", podcastID, userID).
|
||||
Where("id=?", podcastID).
|
||||
Delete(db.Podcast{}).
|
||||
Error
|
||||
if err != nil {
|
||||
@@ -535,3 +502,11 @@ func (p *Podcasts) DeletePodcastEpisode(podcastEpisodeID int) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func pathSafe(in string) string {
|
||||
return filepath.Clean(strings.ReplaceAll(in, string(filepath.Separator), "_"))
|
||||
}
|
||||
|
||||
func absPath(base string, p *db.Podcast) string {
|
||||
return filepath.Join(base, pathSafe(p.Title))
|
||||
}
|
||||
|
||||
@@ -169,43 +169,45 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="padded box">
|
||||
<div class="box-title">
|
||||
<i class="mdi mdi-rss-box"></i> podcasts
|
||||
</div>
|
||||
<div class="box-description text-light">
|
||||
<p>you can add podcasts rss feeds here</p>
|
||||
</div>
|
||||
<div class="block-right">
|
||||
<table id="podcast-preferences">
|
||||
{{ range $pref := .Podcasts }}
|
||||
{{ if .User.IsAdmin }}
|
||||
<div class="padded box">
|
||||
<div class="box-title">
|
||||
<i class="mdi mdi-rss-box"></i> podcasts
|
||||
</div>
|
||||
<div class="box-description text-light">
|
||||
<p>you can add podcasts rss feeds here</p>
|
||||
</div>
|
||||
<div class="block-right">
|
||||
<table id="podcast-preferences">
|
||||
{{ range $pref := .Podcasts }}
|
||||
<tr>
|
||||
<form id="podcast-{{ $pref.ID }}-download" action="{{ printf "/admin/download_podcast_do?id=%d" $pref.ID | path }}" method="post"></form>
|
||||
<form id="podcast-{{ $pref.ID }}-auto-download" action="{{ printf "/admin/update_podcast_do?id=%d" $pref.ID | path }}" method="post"></form>
|
||||
<form id="podcast-{{ $pref.ID }}-delete" action="{{ printf "/admin/delete_podcast_do?id=%d" $pref.ID | path }}" method="post"></form>
|
||||
<td>{{ $pref.Title }}</td>
|
||||
<td><select form="podcast-{{ $pref.ID }}-auto-download" name="setting">
|
||||
{{ if eq $pref.AutoDownload "latest" }}
|
||||
<option value="latest" selected="selected">download latest</option>
|
||||
<option value="none">no auto download</option>
|
||||
{{ else }}
|
||||
<option value="none" selected="selected" >no auto download</option>
|
||||
<option value="latest">download latest</option>
|
||||
{{ end }}
|
||||
</select></td>
|
||||
<td><input form="podcast-{{ $pref.ID }}-download" type="submit" value="download all"></td>
|
||||
<td><input form="podcast-{{ $pref.ID }}-auto-download" type="submit" value="save"></td>
|
||||
<td><input form="podcast-{{ $pref.ID }}-delete" type="submit" value="delete"></td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
<tr>
|
||||
<form id="podcast-{{ $pref.ID }}-download" action="{{ printf "/admin/download_podcast_do?id=%d" $pref.ID | path }}" method="post"></form>
|
||||
<form id="podcast-{{ $pref.ID }}-auto-download" action="{{ printf "/admin/update_podcast_do?id=%d" $pref.ID | path }}" method="post"></form>
|
||||
<form id="podcast-{{ $pref.ID }}-delete" action="{{ printf "/admin/delete_podcast_do?id=%d" $pref.ID | path }}" method="post"></form>
|
||||
<td>{{ $pref.Title }}</td>
|
||||
<td><select form="podcast-{{ $pref.ID }}-auto-download" name="setting">
|
||||
{{ if eq $pref.AutoDownload "latest" }}
|
||||
<option value="latest" selected="selected">download latest</option>
|
||||
<option value="none">no auto download</option>
|
||||
{{ else }}
|
||||
<option value="none" selected="selected" >no auto download</option>
|
||||
<option value="latest">download latest</option>
|
||||
{{ end }}
|
||||
</select></td>
|
||||
<td><input form="podcast-{{ $pref.ID }}-download" type="submit" value="download all"></td>
|
||||
<td><input form="podcast-{{ $pref.ID }}-auto-download" type="submit" value="save"></td>
|
||||
<td><input form="podcast-{{ $pref.ID }}-delete" type="submit" value="delete"></td>
|
||||
<form id="podcast-add" action="{{ path "/admin/add_podcast_do" }}" method="post"></form>
|
||||
<td><input form="podcast-add" type="text" name="feed" placeholder="rss feed url"></td>
|
||||
<td><input form="podcast-add" type="submit" value="save"></td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
<tr>
|
||||
<form id="podcast-add" action="{{ path "/admin/add_podcast_do" }}" method="post"></form>
|
||||
<td><input form="podcast-add" type="text" name="feed" placeholder="rss feed url"></td>
|
||||
<td><input form="podcast-add" type="submit" value="save"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="padded box">
|
||||
<div class="box-title">
|
||||
<i class="mdi mdi-playlist-music"></i> playlists
|
||||
|
||||
@@ -391,7 +391,6 @@ func (c *Controller) ServeDeleteTranscodePrefDo(r *http.Request) *Response {
|
||||
}
|
||||
|
||||
func (c *Controller) ServePodcastAddDo(r *http.Request) *Response {
|
||||
user := r.Context().Value(CtxUser).(*db.User)
|
||||
rssURL := r.FormValue("feed")
|
||||
fp := gofeed.NewParser()
|
||||
feed, err := fp.ParseURL(rssURL)
|
||||
@@ -401,7 +400,7 @@ func (c *Controller) ServePodcastAddDo(r *http.Request) *Response {
|
||||
flashW: []string{fmt.Sprintf("could not create feed: %v", err)},
|
||||
}
|
||||
}
|
||||
if _, err = c.Podcasts.AddNewPodcast(rssURL, feed, user.ID); err != nil {
|
||||
if _, err = c.Podcasts.AddNewPodcast(rssURL, feed); err != nil {
|
||||
return &Response{
|
||||
redirect: "/admin/home",
|
||||
flashW: []string{fmt.Sprintf("could not create feed: %v", err)},
|
||||
@@ -454,12 +453,11 @@ func (c *Controller) ServePodcastUpdateDo(r *http.Request) *Response {
|
||||
}
|
||||
|
||||
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{code: 400, err: "please provide a valid podcast id"}
|
||||
}
|
||||
if err := c.Podcasts.DeletePodcast(user.ID, id); err != nil {
|
||||
if err := c.Podcasts.DeletePodcast(id); err != nil {
|
||||
return &Response{code: 400, err: "please provide a valid podcast id"}
|
||||
}
|
||||
return &Response{
|
||||
|
||||
@@ -14,9 +14,8 @@ import (
|
||||
func (c *Controller) ServeGetPodcasts(r *http.Request) *spec.Response {
|
||||
params := r.Context().Value(CtxParams).(params.Params)
|
||||
isIncludeEpisodes := params.GetOrBool("includeEpisodes", true)
|
||||
user := r.Context().Value(CtxUser).(*db.User)
|
||||
id, _ := params.GetID("id")
|
||||
podcasts, err := c.Podcasts.GetPodcastOrAll(user.ID, id.Value, isIncludeEpisodes)
|
||||
podcasts, err := c.Podcasts.GetPodcastOrAll(id.Value, isIncludeEpisodes)
|
||||
if err != nil {
|
||||
return spec.NewError(10, "failed get podcast(s): %s", err)
|
||||
}
|
||||
@@ -45,6 +44,10 @@ func (c *Controller) ServeGetNewestPodcasts(r *http.Request) *spec.Response {
|
||||
}
|
||||
|
||||
func (c *Controller) ServeDownloadPodcastEpisode(r *http.Request) *spec.Response {
|
||||
user := r.Context().Value(CtxUser).(*db.User)
|
||||
if (!user.IsAdmin) {
|
||||
return spec.NewError(10, "user not admin")
|
||||
}
|
||||
params := r.Context().Value(CtxParams).(params.Params)
|
||||
id, err := params.GetID("id")
|
||||
if err != nil || id.Type != specid.PodcastEpisode {
|
||||
@@ -58,6 +61,9 @@ func (c *Controller) ServeDownloadPodcastEpisode(r *http.Request) *spec.Response
|
||||
|
||||
func (c *Controller) ServeCreatePodcastChannel(r *http.Request) *spec.Response {
|
||||
user := r.Context().Value(CtxUser).(*db.User)
|
||||
if (!user.IsAdmin) {
|
||||
return spec.NewError(10, "user not admin")
|
||||
}
|
||||
params := r.Context().Value(CtxParams).(params.Params)
|
||||
rssURL, _ := params.Get("url")
|
||||
fp := gofeed.NewParser()
|
||||
@@ -65,7 +71,7 @@ func (c *Controller) ServeCreatePodcastChannel(r *http.Request) *spec.Response {
|
||||
if err != nil {
|
||||
return spec.NewError(10, "failed to parse feed: %s", err)
|
||||
}
|
||||
if _, err = c.Podcasts.AddNewPodcast(rssURL, feed, user.ID); err != nil {
|
||||
if _, err = c.Podcasts.AddNewPodcast(rssURL, feed); err != nil {
|
||||
return spec.NewError(10, "failed to add feed: %s", err)
|
||||
}
|
||||
return spec.NewResponse()
|
||||
@@ -73,7 +79,10 @@ func (c *Controller) ServeCreatePodcastChannel(r *http.Request) *spec.Response {
|
||||
|
||||
func (c *Controller) ServeRefreshPodcasts(r *http.Request) *spec.Response {
|
||||
user := r.Context().Value(CtxUser).(*db.User)
|
||||
if err := c.Podcasts.RefreshPodcastsForUser(user.ID); err != nil {
|
||||
if (!user.IsAdmin) {
|
||||
return spec.NewError(10, "user not admin")
|
||||
}
|
||||
if err := c.Podcasts.RefreshPodcasts(); err != nil {
|
||||
return spec.NewError(10, "failed to refresh feeds: %s", err)
|
||||
}
|
||||
return spec.NewResponse()
|
||||
@@ -81,18 +90,25 @@ func (c *Controller) ServeRefreshPodcasts(r *http.Request) *spec.Response {
|
||||
|
||||
func (c *Controller) ServeDeletePodcastChannel(r *http.Request) *spec.Response {
|
||||
user := r.Context().Value(CtxUser).(*db.User)
|
||||
if (!user.IsAdmin) {
|
||||
return spec.NewError(10, "user not admin")
|
||||
}
|
||||
params := r.Context().Value(CtxParams).(params.Params)
|
||||
id, err := params.GetID("id")
|
||||
if err != nil || id.Type != specid.Podcast {
|
||||
return spec.NewError(10, "please provide a valid podcast id")
|
||||
}
|
||||
if err := c.Podcasts.DeletePodcast(user.ID, id.Value); err != nil {
|
||||
if err := c.Podcasts.DeletePodcast(id.Value); err != nil {
|
||||
return spec.NewError(10, "failed to delete podcast: %s", err)
|
||||
}
|
||||
return spec.NewResponse()
|
||||
}
|
||||
|
||||
func (c *Controller) ServeDeletePodcastEpisode(r *http.Request) *spec.Response {
|
||||
user := r.Context().Value(CtxUser).(*db.User)
|
||||
if (!user.IsAdmin) {
|
||||
return spec.NewError(10, "user not admin")
|
||||
}
|
||||
params := r.Context().Value(CtxParams).(params.Params)
|
||||
id, err := params.GetID("id")
|
||||
if err != nil || id.Type != specid.PodcastEpisode {
|
||||
|
||||
@@ -169,10 +169,6 @@ 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))
|
||||
routUser.Handle("/add_podcast_do", ctrl.H(ctrl.ServePodcastAddDo))
|
||||
routUser.Handle("/delete_podcast_do", ctrl.H(ctrl.ServePodcastDeleteDo))
|
||||
routUser.Handle("/download_podcast_do", ctrl.H(ctrl.ServePodcastDownloadDo))
|
||||
routUser.Handle("/update_podcast_do", ctrl.H(ctrl.ServePodcastUpdateDo))
|
||||
|
||||
// admin routes (if session is valid, and is admin)
|
||||
routAdmin := routUser.NewRoute().Subrouter()
|
||||
@@ -189,6 +185,10 @@ func setupAdmin(r *mux.Router, ctrl *ctrladmin.Controller) {
|
||||
routAdmin.Handle("/update_lastfm_api_key_do", ctrl.H(ctrl.ServeUpdateLastFMAPIKeyDo))
|
||||
routAdmin.Handle("/start_scan_inc_do", ctrl.H(ctrl.ServeStartScanIncDo))
|
||||
routAdmin.Handle("/start_scan_full_do", ctrl.H(ctrl.ServeStartScanFullDo))
|
||||
routAdmin.Handle("/add_podcast_do", ctrl.H(ctrl.ServePodcastAddDo))
|
||||
routAdmin.Handle("/delete_podcast_do", ctrl.H(ctrl.ServePodcastDeleteDo))
|
||||
routAdmin.Handle("/download_podcast_do", ctrl.H(ctrl.ServePodcastDownloadDo))
|
||||
routAdmin.Handle("/update_podcast_do", ctrl.H(ctrl.ServePodcastUpdateDo))
|
||||
|
||||
// middlewares should be run for not found handler
|
||||
// https://github.com/gorilla/mux/issues/416
|
||||
|
||||
Reference in New Issue
Block a user