diff --git a/cmd/seeder/seeder.go b/cmd/seeder/seeder.go index d933dcd..231d522 100644 --- a/cmd/seeder/seeder.go +++ b/cmd/seeder/seeder.go @@ -44,7 +44,7 @@ func SeedDb() error { playlistSongsRepo = db.NewBaseDB[models.PlaylistSong](dbConn) playlistOwnerRepo = db.NewBaseDB[models.PlaylistOwner](dbConn) - playlistService := playlistspkg.New(playlistRepo, playlistOwnerRepo, nil, nil) + playlistService := playlistspkg.New(playlistRepo, playlistOwnerRepo, nil) pl, err := playlistService.GetAll(400) if err != nil { diff --git a/cmd/server/server.go b/cmd/server/server.go index 3d27a11..27a6511 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -42,7 +42,7 @@ func StartServer(staticFS embed.FS) error { playlistSongsRepo := db.NewBaseDB[models.PlaylistSong](dbConn) downloadService := download.New(songRepo) - playlistsService := playlists.New(playlistRepo, playlistOwnersRepo, playlistSongsRepo, downloadService) + playlistsService := playlists.New(playlistRepo, playlistOwnersRepo, playlistSongsRepo) songsService := songs.New(playlistSongsRepo, playlistOwnersRepo, songRepo, playlistRepo, downloadService) jwtUtil := jwt.NewJWTImpl() @@ -67,7 +67,7 @@ func StartServer(staticFS embed.FS) error { }) pagesHandler.Handle("/music/", http.StripPrefix("/music", http.FileServer(http.Dir(config.Env().YouTube.MusicDir)))) - pagesRouter := pages.NewPagesHandler(profileRepo, playlistsService, jwtUtil, &search.ScraperSearch{}) + pagesRouter := pages.NewPagesHandler(profileRepo, playlistsService, jwtUtil, &search.ScraperSearch{}, downloadService) pagesHandler.HandleFunc("/", gHandler.OptionalAuthPage(pagesRouter.HandleHomePage)) pagesHandler.HandleFunc("/signup", gHandler.AuthPage(pagesRouter.HandleSignupPage)) pagesHandler.HandleFunc("/login", gHandler.AuthPage(pagesRouter.HandleLoginPage)) diff --git a/handlers/apis/playlist.go b/handlers/apis/playlist.go index 397d6a4..c2dbb3f 100644 --- a/handlers/apis/playlist.go +++ b/handlers/apis/playlist.go @@ -69,31 +69,17 @@ func (p *playlistApi) HandleToggleSongInPlaylist(w http.ResponseWriter, r *http. w.WriteHeader(http.StatusBadRequest) return } - removeSongFromPlaylist := r.URL.Query().Get("remove") - if removeSongFromPlaylist != "true" && removeSongFromPlaylist != "false" { - w.WriteHeader(http.StatusBadRequest) - return - } - - var err error - switch removeSongFromPlaylist { - case "false": - err = p.songService.AddSongToPlaylist(songId, playlistId, profileId) - case "true": - err = p.songService.RemoveSongFromPlaylist(songId, playlistId, profileId) - } + added, err := p.songService.ToggleSongInPlaylist(songId, playlistId, profileId) if err != nil { w.WriteHeader(http.StatusInternalServerError) log.Errorln(err) return } - // TODO: idk, this is ugly, but it works lol - switch removeSongFromPlaylist { - case "false": - w.Write([]byte("
")) - case "true": - w.Write([]byte("
")) + if added { + _, _ = w.Write([]byte("
")) + } else { + _, _ = w.Write([]byte("
")) } } diff --git a/handlers/pages/pages.go b/handlers/pages/pages.go index a899521..9323228 100644 --- a/handlers/pages/pages.go +++ b/handlers/pages/pages.go @@ -9,6 +9,7 @@ import ( "dankmuzikk/models" "dankmuzikk/services/jwt" "dankmuzikk/services/playlists" + "dankmuzikk/services/youtube/download" "dankmuzikk/services/youtube/search" "dankmuzikk/views/layouts" "dankmuzikk/views/pages" @@ -26,6 +27,7 @@ type pagesHandler struct { playlistsService *playlists.Service jwtUtil jwt.Manager[jwt.Json] ytSearch search.Service + downloadService *download.Service } func NewPagesHandler( @@ -33,12 +35,14 @@ func NewPagesHandler( playlistsService *playlists.Service, jwtUtil jwt.Manager[jwt.Json], ytSearch search.Service, + downloadService *download.Service, ) *pagesHandler { return &pagesHandler{ profileRepo: profileRepo, playlistsService: playlistsService, jwtUtil: jwtUtil, ytSearch: ytSearch, + downloadService: downloadService, } } @@ -151,11 +155,15 @@ func (p *pagesHandler) HandleSearchResultsPage(w http.ResponseWriter, r *http.Re return } - var songsInPlaylists map[string]string + if len(results) != 0 { + // TODO: move this call out of here + log.Info("downloading songs' meta data from search") + _ = p.downloadService.DownloadYoutubeSongsMetadata(results) + } + var songsInPlaylists map[string]bool var playlists []entities.Playlist profileId, profileIdCorrect := r.Context().Value(handlers.ProfileIdKey).(uint) if profileIdCorrect { - log.Info("downloading songs' meta data from search") playlists, songsInPlaylists, _ = p.playlistsService.GetAllMappedForAddPopover(results, profileId) } diff --git a/services/playlists/playlists.go b/services/playlists/playlists.go index b15f029..1818aae 100644 --- a/services/playlists/playlists.go +++ b/services/playlists/playlists.go @@ -4,7 +4,6 @@ import ( "dankmuzikk/db" "dankmuzikk/entities" "dankmuzikk/models" - "dankmuzikk/services/youtube/download" "fmt" "strings" "time" @@ -18,7 +17,6 @@ type Service struct { repo db.UnsafeCRUDRepo[models.Playlist] playlistOwnersRepo db.CRUDRepo[models.PlaylistOwner] playlistSongsRepo db.UnsafeCRUDRepo[models.PlaylistSong] - downloadService *download.Service } // New accepts a playlist repo, a playlist pwners, and returns a new instance to the playlists service. @@ -26,9 +24,8 @@ func New( repo db.UnsafeCRUDRepo[models.Playlist], playlistOwnersRepo db.CRUDRepo[models.PlaylistOwner], playlistSongsRepo db.UnsafeCRUDRepo[models.PlaylistSong], - downloadService *download.Service, ) *Service { - return &Service{repo, playlistOwnersRepo, playlistSongsRepo, downloadService} + return &Service{repo, playlistOwnersRepo, playlistSongsRepo} } // CreatePlaylist creates a new playlist with with provided details for the given account's profile. @@ -216,9 +213,7 @@ func (p *Service) GetAll(ownerId uint) ([]entities.Playlist, error) { } // TODO: fix this weird ass 3 return values -func (p *Service) GetAllMappedForAddPopover(songs []entities.Song, ownerId uint) ([]entities.Playlist, map[string]string, error) { - _ = p.downloadService.DownloadYoutubeSongsMetadata(songs) - +func (p *Service) GetAllMappedForAddPopover(songs []entities.Song, ownerId uint) ([]entities.Playlist, map[string]bool, error) { var dbPlaylists []models.Playlist err := p. repo. @@ -238,19 +233,19 @@ func (p *Service) GetAllMappedForAddPopover(songs []entities.Song, ownerId uint) return nil, nil, ErrUnauthorizedToSeePlaylist } - mappedPlaylists := make(map[string]string) - usedPlaylists := make(map[string]bool) + mappedPlaylists := make(map[string]bool) for _, playlist := range dbPlaylists { for _, song := range playlist.Songs { - mappedPlaylists[song.YtId] = playlist.PublicId - usedPlaylists[playlist.PublicId] = true + mappedPlaylists[song.YtId+"-"+playlist.PublicId] = true } } - for i := 0; i < len(dbPlaylists); i++ { - if usedPlaylists[dbPlaylists[i].PublicId] { - continue + for i, playlist := range dbPlaylists { + for _, song := range playlist.Songs { + if mappedPlaylists[song.YtId+"-"+dbPlaylists[0].PublicId] { + continue + } + mappedPlaylists[fmt.Sprintf("unmapped-%d", i)] = false } - mappedPlaylists[fmt.Sprintf("unmapped-%d", i)] = dbPlaylists[i].PublicId } playlists := make([]entities.Playlist, len(dbPlaylists)) diff --git a/services/playlists/songs/songs.go b/services/playlists/songs/songs.go index 5ea0ac8..d7eb238 100644 --- a/services/playlists/songs/songs.go +++ b/services/playlists/songs/songs.go @@ -4,6 +4,7 @@ import ( "dankmuzikk/db" "dankmuzikk/models" "dankmuzikk/services/youtube/download" + "errors" ) // Service represents songs in platlists management service, @@ -33,38 +34,41 @@ func New( } } -// AddSongToPlaylist adds a given song to the given playlist, -// checks if the actual song and playlist exist then adds the song to the given playlist, +// ToggleSongInPlaylist adds/removes a given song to/from the given playlist, +// checks if the actual song and playlist exist then adds/removes the song to/from the given playlist, // and returns an occurring error. -// TODO: check playlist's owner :) -func (s *Service) AddSongToPlaylist(songId, playlistPubId string, ownerId uint) error { +func (s *Service) ToggleSongInPlaylist(songId, playlistPubId string, ownerId uint) (added bool, err error) { playlist, err := s.playlistRepo.GetByConds("public_id = ?", playlistPubId) if err != nil { - return err + return } _, err = s.playlistOwnerRepo.GetByConds("profile_id = ? AND playlist_id = ?", ownerId, playlist[0].Id) if err != nil { - return err + return } song, err := s.songRepo.GetByConds("yt_id = ?", songId) if err != nil { - return err + return } - - err = s.playlistSongRepo.Add(&models.PlaylistSong{ - PlaylistId: playlist[0].Id, - SongId: song[0].Id, - }) - if err != nil { - return err + _, err = s.playlistSongRepo.GetByConds("playlist_id = ? AND song_id = ?", playlist[0].Id, song[0].Id) + if errors.Is(err, db.ErrRecordNotFound) { + err = s.playlistSongRepo.Add(&models.PlaylistSong{ + PlaylistId: playlist[0].Id, + SongId: song[0].Id, + }) + if err != nil { + return + } + return true, s.downloadService.DownloadYoutubeSongQueue(songId) + } else { + return false, s. + playlistSongRepo. + Delete("playlist_id = ? AND song_id = ?", playlist[0].Id, song[0].Id) } - - return s.downloadService.DownloadYoutubeSongQueue(songId) } // IncrementSongPlays increases the song's play times in the given playlist. // Checks for the song and playlist first, yada yada... -// TODO: check playlist's owner :) func (s *Service) IncrementSongPlays(songId, playlistPubId string, ownerId uint) error { var playlist models.Playlist err := s. @@ -118,26 +122,3 @@ func (s *Service) IncrementSongPlays(songId, playlistPubId string, ownerId uint) Update("play_times", ps.PlayTimes+1). Error } - -// RemoveSongFromPlaylist removes a given song from the given playlist, -// checks if the actual song and playlist exist then removes the song to the given playlist, -// and returns an occurring error. -// TODO: check playlist's owner :) -func (s *Service) RemoveSongFromPlaylist(songId, playlistPubId string, ownerId uint) error { - playlist, err := s.playlistRepo.GetByConds("public_id = ?", playlistPubId) - if err != nil { - return err - } - _, err = s.playlistOwnerRepo.GetByConds("profile_id = ? AND playlist_id = ?", ownerId, playlist[0].Id) - if err != nil { - return err - } - song, err := s.songRepo.GetByConds("yt_id = ?", songId) - if err != nil { - return err - } - - return s. - playlistSongRepo. - Delete("playlist_id = ? AND song_id = ?", playlist[0].Id, song[0].Id) -} diff --git a/services/youtube/download/download.go b/services/youtube/download/download.go index 1775277..62b1d44 100644 --- a/services/youtube/download/download.go +++ b/services/youtube/download/download.go @@ -8,6 +8,7 @@ import ( "dankmuzikk/models" "errors" "fmt" + "math" "net/http" "os" ) @@ -99,5 +100,12 @@ func (d *Service) DownloadYoutubeSongsMetadata(songs []entities.Song) error { } } + for i := 0; i < int(math.Min(float64(len(songs)), 5)); i++ { + err := d.DownloadYoutubeSongQueue(songs[i].YtId) + if err != nil { + log.Errorln(err) + } + } + return nil } diff --git a/views/components/header/header.templ b/views/components/header/header.templ index d0d313c..ec821db 100644 --- a/views/components/header/header.templ +++ b/views/components/header/header.templ @@ -82,7 +82,7 @@ templ desktopHeader() { templ Header() {
// mobiles are usually shitty at rendering, so this prevents mobiles from rendering two blocks and choosing one using CSS. - if ctx.Value("is-mobile").(bool) { + if isMobile, ok := ctx.Value("is-mobile").(bool); ok && isMobile { @mobileHeader() } else {
diff --git a/views/components/player/player.templ b/views/components/player/player.templ index 539aa2d..9846def 100644 --- a/views/components/player/player.templ +++ b/views/components/player/player.templ @@ -2,7 +2,7 @@ package player templ PlayerSticky() { @@ -98,23 +104,50 @@ templ songOptionsToggle() { } +//if isMobile, ok := ctx.Value("is-mobile").(bool); ok && isMobile { templ songOptions(song entities.Song, playlistId string) {
+

Options

+ +
+} + +templ songOptionsMobile(song entities.Song, playlistId string) { +
-

Song's options

+

Details and options

+

Added on { song.AddedAt }

+ if song.PlayTimes == 1 { +

Played once

+ } else if song.PlayTimes > 1 { +

Played { fmt.Sprint( song.PlayTimes) } times

+ } + >Remove from playlist
} diff --git a/views/pages/search_results.templ b/views/pages/search_results.templ index bfd6777..2da46ad 100644 --- a/views/pages/search_results.templ +++ b/views/pages/search_results.templ @@ -5,7 +5,7 @@ import ( "dankmuzikk/entities" ) -templ SearchResults(results []entities.Song, playlists []entities.Playlist, songsInPlaylists map[string]string) { +templ SearchResults(results []entities.Song, playlists []entities.Playlist, songsInPlaylists map[string]bool) {