feat: store and use m3u files on filesystem for playlists

closes #306
closes #307
closes #66
This commit is contained in:
sentriz
2023-04-22 18:25:19 +01:00
committed by Senan Kelly
parent 1d3877668f
commit 7dc9575e52
18 changed files with 621 additions and 355 deletions

View File

@@ -1,16 +1,23 @@
//nolint:goerr113
package db
import (
"errors"
"fmt"
"log"
"path/filepath"
"time"
"github.com/jinzhu/gorm"
"go.senan.xyz/gonic/playlist"
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
"gopkg.in/gormigrate.v1"
)
type MigrationContext struct {
OriginalMusicPath string
PlaylistsPath string
PodcastsPath string
}
func (db *DB) Migrate(ctx MigrationContext) error {
@@ -46,6 +53,7 @@ func (db *DB) Migrate(ctx MigrationContext) error {
construct(ctx, "202206101425", migrateUser),
construct(ctx, "202207251148", migrateStarRating),
construct(ctx, "202211111057", migratePlaylistsQueuesToFullID),
construct(ctx, "202304221528", migratePlaylistsToM3U),
}
return gormigrate.
@@ -82,7 +90,6 @@ func migrateInitSchema(tx *gorm.DB, _ MigrationContext) error {
Setting{},
Play{},
Album{},
Playlist{},
PlayQueue{},
).
Error
@@ -110,6 +117,9 @@ func migrateCreateInitUser(tx *gorm.DB, _ MigrationContext) error {
}
func migrateMergePlaylist(tx *gorm.DB, _ MigrationContext) error {
if !tx.HasTable("playlists") {
return nil
}
if !tx.HasTable("playlist_items") {
return nil
}
@@ -335,7 +345,10 @@ func migrateAlbumRootDirAgain(tx *gorm.DB, ctx MigrationContext) error {
}
func migratePublicPlaylist(tx *gorm.DB, ctx MigrationContext) error {
return tx.AutoMigrate(Playlist{}).Error
if !tx.HasTable("playlists") {
return nil
}
return tx.AutoMigrate(_OldPlaylist{}).Error
}
func migratePodcastDropUserID(tx *gorm.DB, _ MigrationContext) error {
@@ -389,6 +402,10 @@ func migrateStarRating(tx *gorm.DB, _ MigrationContext) error {
}
func migratePlaylistsQueuesToFullID(tx *gorm.DB, _ MigrationContext) error {
if !tx.HasTable("playlists") {
return nil
}
step := tx.Exec(`
UPDATE playlists SET items=('tr-' || items) WHERE items IS NOT NULL;
`)
@@ -441,3 +458,61 @@ func migratePlaylistsQueuesToFullID(tx *gorm.DB, _ MigrationContext) error {
return nil
}
func migratePlaylistsToM3U(tx *gorm.DB, ctx MigrationContext) error {
if ctx.PlaylistsPath == "" || !tx.HasTable("playlists") {
return nil
}
// local copy of specidpaths.Locate to avoid circular dep
locate := func(id specid.ID) string {
switch id.Type {
case specid.Track:
var track Track
tx.Preload("Album").Where("id=?", id.Value).Find(&track)
return track.AbsPath()
case specid.PodcastEpisode:
var pe PodcastEpisode
tx.Where("id=?", id.Value).Find(&pe)
if pe.Path == "" {
return ""
}
return filepath.Join(ctx.PodcastsPath, pe.Path)
}
return ""
}
store, err := playlist.NewStore(ctx.PlaylistsPath)
if err != nil {
return fmt.Errorf("create playlists store: %w", err)
}
var prevs []*_OldPlaylist
if err := tx.Find(&prevs).Error; err != nil {
return fmt.Errorf("fetch old playlists: %w", err)
}
for _, prev := range prevs {
var pl playlist.Playlist
pl.UpdatedAt = time.Now()
pl.UserID = prev.UserID
pl.Name = prev.Name
pl.Comment = prev.Comment
pl.IsPublic = prev.IsPublic
for _, id := range splitIDs(prev.Items, ",") {
path := locate(id)
if path == "" {
log.Printf("migrating: can't find item %s from playlist %q on filesystem", id, prev.Name)
continue
}
pl.Items = append(pl.Items, path)
}
if err := store.Write(playlist.NewPath(prev.UserID, prev.Name), &pl); err != nil {
return fmt.Errorf("write playlist: %w", err)
}
}
return nil
}