From cd4b7db0db2ce91c58b7ba98ed79150968c07e4a Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Thu, 16 Oct 2025 23:18:50 +0200 Subject: [PATCH 01/17] wip --- .../src/lib/components/Song.svelte | 137 +++++++++++------- MyMusicClientSveltePwa/src/lib/scripts/api.js | 26 ++++ .../src/lib/scripts/playlistService.js | 18 ++- 3 files changed, 124 insertions(+), 57 deletions(-) diff --git a/MyMusicClientSveltePwa/src/lib/components/Song.svelte b/MyMusicClientSveltePwa/src/lib/components/Song.svelte index 314e957..af52c19 100644 --- a/MyMusicClientSveltePwa/src/lib/components/Song.svelte +++ b/MyMusicClientSveltePwa/src/lib/components/Song.svelte @@ -3,9 +3,11 @@ import { getContext, onMount, setContext } from "svelte"; import { currentSong, isPlaying, isLoading } from "../scripts/playbackService"; - import { getImageUrl } from "../scripts/api"; + import { getImageUrl, deleteSongFromPlaylist } from "../scripts/api"; + import { removeSongFromPlaylist } from "../scripts/playlistService"; export let song; + export let playlistId; let playOrPauseSong; @@ -13,87 +15,110 @@ playOrPauseSong = getContext("playOrPauseSong"); }); + async function deleteSong() { + if(confirm(`Are you sure you want to delete the song "${song.name}" from this playlist?`)) { + var deleted = await deleteSongFromPlaylist(playlistId, song.id); + + if(deleted) { + removeSongFromPlaylist(song.id); + } else { + alert(`Failed to delete song with ID: ${song.id} from playlist ID: ${playlistId}`); + } + } + } + $: $isPlaying; $: $currentSong; $: $isLoading; {#if song} -
-
-
-
{song.name}
+
+
+
+
+ +
+
+
{song.name}
+
+
+ +
+
- -
{:else}

No song available.

{/if} diff --git a/MyMusicClientSveltePwa/src/lib/scripts/api.js b/MyMusicClientSveltePwa/src/lib/scripts/api.js index be2f19f..3b81383 100644 --- a/MyMusicClientSveltePwa/src/lib/scripts/api.js +++ b/MyMusicClientSveltePwa/src/lib/scripts/api.js @@ -104,6 +104,32 @@ export async function deletePlaylist(playlistId) { } } +export async function deleteSongFromPlaylist(playlistId, songId) { + try { + const response = await fetch(`${baseApiUrl}/playlistsong/${playlistId}/${songId}`, { + method: "DELETE", + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response}`); + } + + console.log("Delete song response:", response); + + return { + success: true, + data: response, + }; + + } catch (error) { + console.error("Error deleting song from playlist:", error); + return { + success: false, + data: error, + }; + } +} + export function getImageUrl(path) { var config = getConfiguration(); diff --git a/MyMusicClientSveltePwa/src/lib/scripts/playlistService.js b/MyMusicClientSveltePwa/src/lib/scripts/playlistService.js index fbf69df..5caa067 100644 --- a/MyMusicClientSveltePwa/src/lib/scripts/playlistService.js +++ b/MyMusicClientSveltePwa/src/lib/scripts/playlistService.js @@ -2,7 +2,7 @@ import { writable, get } from "svelte/store"; import { getCachedPlaylists, setCachedPlaylists, setPlaylistSongs, getCachedPlaylistSongs, appConfiguration, getConfiguration, getCurrentPlaylistId } from "./storageService"; import { fetchPlaylists, fetchPlaylistSongs, fetchNewPlaylist, fetchNewPlaylistSongs, deletePlaylist } from "./api"; import { componentParams, navigateTo } from "./routeService"; -import { playOrPauseSong, setPlaylists, currentSong, playPercentage } from "./playbackService"; +import { playOrPauseSong, setPlaylists, currentSong, playPercentage, updateCurrentPlaylist } from "./playbackService"; export const playlistsStore = writable([]); @@ -92,6 +92,22 @@ export async function deleteCurrentPlaylist() { } } +export function removeSongFromPlaylist(songId) { + const cachedPlaylists = getCachedPlaylists(); + + // remove song from playlist + for (const playlist of cachedPlaylists) { + const playlistId = playlist.id; + const cachedSongs = getCachedPlaylistSongs(playlistId); + const songIndex = cachedSongs.findIndex((s) => s.id === songId); + if (songIndex !== -1) { + cachedSongs.splice(songIndex, 1); + setPlaylistSongs(playlistId, cachedSongs); + } + updateCurrentPlaylist(playlistId); + } +} + async function backgroundUpdate() { // update playlists in the background const lastItemInex = -1; From 202a63f1d914998d1e7a0383300dd73fdd593bca Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Thu, 16 Oct 2025 21:19:11 +0000 Subject: [PATCH 02/17] wip 2 --- MyMusicBoxApi/configuration/util.go | 10 ++++++++++ MyMusicBoxApi/database/db.go | 4 ++-- MyMusicBoxApi/database/songtable.go | 30 +++++++++++++++++++++++++++++ MyMusicBoxApi/http/playlistsong.go | 27 ++++++++++++++++++++++++++ MyMusicBoxApi/reset | 3 +++ 5 files changed, 72 insertions(+), 2 deletions(-) diff --git a/MyMusicBoxApi/configuration/util.go b/MyMusicBoxApi/configuration/util.go index 0fdc408..250cd94 100644 --- a/MyMusicBoxApi/configuration/util.go +++ b/MyMusicBoxApi/configuration/util.go @@ -3,7 +3,9 @@ package configuration import ( "flag" "fmt" + "musicboxapi/logging" "musicboxapi/models" + "os" ) var Config models.Config @@ -24,5 +26,13 @@ func GetApiGroupUrl(version string) string { } else { return fmt.Sprintf("/api/%s", version) } +} + +func DeleteFile(path string) { + err := os.Remove(path) + if err != nil { + logging.ErrorStackTrace(err) + logging.Error(fmt.Sprintf("Failed to delete file, path: %s", path)) + } } diff --git a/MyMusicBoxApi/database/db.go b/MyMusicBoxApi/database/db.go index 5743ef6..aafc63a 100644 --- a/MyMusicBoxApi/database/db.go +++ b/MyMusicBoxApi/database/db.go @@ -151,8 +151,8 @@ func (base *BaseTable) NonScalarQuery(query string, params ...any) (error error) return nil } -func (base *BaseTable) QueryRow(query string) *sql.Row { - return base.DB.QueryRow(query) +func (base *BaseTable) QueryRow(query string, params ...any) *sql.Row { + return base.DB.QueryRow(query, params...) } func (base *BaseTable) QueryRowsContex(ctx context.Context, query string, params ...any) (*sql.Rows, error) { diff --git a/MyMusicBoxApi/database/songtable.go b/MyMusicBoxApi/database/songtable.go index b39be23..59a1fc7 100644 --- a/MyMusicBoxApi/database/songtable.go +++ b/MyMusicBoxApi/database/songtable.go @@ -10,6 +10,8 @@ import ( type ISongTable interface { InsertSong(song *models.Song) (err error) FetchSongs(ctx context.Context) (songs []models.Song, err error) + FetchSongById(songId int) (song models.Song, err error) + DeleteSongById(songId int) (err error) } type SongTable struct { @@ -47,6 +49,29 @@ func (table *SongTable) InsertSong(song *models.Song) (error error) { return err } +func (table *SongTable) FetchSongById(songId int) (song models.Song, err error) { + + query := "SELECT Id, Name, Path, ThumbnailPath, Duration, SourceId, UpdatedAt, CreatedAt FROM Song WHERE Id = $1" + + row := table.QueryRow(query, songId) + + _err := row.Err() + + if _err != nil { + logging.ErrorStackTrace(_err) + return models.Song{}, _err + } + + _err = row.Scan(&song.Id, &song.Name, &song.Path, &song.ThumbnailPath, &song.Duration, &song.SourceId, &song.UpdatedAt, &song.CreatedAt) + + if _err != nil { + logging.ErrorStackTrace(_err) + return models.Song{}, _err + } + + return song, nil +} + func (table *SongTable) FetchSongs(ctx context.Context) (songs []models.Song, error error) { query := "SELECT Id, Name, Path, ThumbnailPath, Duration, SourceId, UpdatedAt, CreatedAt FROM Song" // order by? @@ -77,3 +102,8 @@ func (table *SongTable) FetchSongs(ctx context.Context) (songs []models.Song, er return songs, nil } + +func (table *SongTable) DeleteSongById(songId int) (err error) { + query := "DELETE FROM Song WHERE Id = $1" + return table.NonScalarQuery(query, songId) +} diff --git a/MyMusicBoxApi/http/playlistsong.go b/MyMusicBoxApi/http/playlistsong.go index 455df23..e2bc995 100644 --- a/MyMusicBoxApi/http/playlistsong.go +++ b/MyMusicBoxApi/http/playlistsong.go @@ -2,6 +2,7 @@ package http import ( "fmt" + "musicboxapi/configuration" "musicboxapi/database" "musicboxapi/models" "net/http" @@ -14,6 +15,8 @@ type PlaylistSongHandler struct { PlaylistsongTable database.IPlaylistsongTable } +const DefaultPlaylistId = 1 + // @Produce json // @Param playlistId path int true "Id of playlist" // @Param lastKnowSongPosition path int false "Last song that is know by the client, pass this in to only get the latest songs" @@ -99,5 +102,29 @@ func (handler *PlaylistSongHandler) DeletePlaylistSong(ctx *gin.Context) { ctx.JSON(http.StatusInternalServerError, models.ErrorResponse(err)) return } + + if playlistId == DefaultPlaylistId { + + songTable := database.NewSongTableInstance() + song, err := songTable.FetchSongById(songId) + + if err != nil { + ctx.JSON(http.StatusInternalServerError, models.ErrorResponse(err)) + return + } + + // Delete actual song file + audioFilePath := song.Path + configuration.DeleteFile(audioFilePath) + + // Delete actual thumbnail file + thumbnail := song.ThumbnailPath + thumbnailPath := fmt.Sprintf("%s/images/%s", configuration.Config.SourceFolder, thumbnail) + configuration.DeleteFile(thumbnailPath) + + // Delete from database + songTable.DeleteSongById(song.Id) + } + ctx.Status(http.StatusOK) } diff --git a/MyMusicBoxApi/reset b/MyMusicBoxApi/reset index 0d361cb..a4442e7 100755 --- a/MyMusicBoxApi/reset +++ b/MyMusicBoxApi/reset @@ -1,5 +1,8 @@ #!/bin/bash +# delete dev music and images +sudo rm -r music_dev + # stop dev docker postgres cd .. cd dev_database From c26eaa0e5be7917f4e33fdba4718017db3ddb243 Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Sun, 19 Oct 2025 01:03:26 +0200 Subject: [PATCH 03/17] beta test --- .../src/lib/components/Song.svelte | 28 ++++++++++----- .../src/lib/pages/Playlists.svelte | 7 ++-- .../src/lib/scripts/playbackService.js | 6 ++++ .../src/lib/scripts/playlistService.js | 35 +++++++++++-------- .../src/lib/scripts/storageService.js | 2 +- 5 files changed, 50 insertions(+), 28 deletions(-) diff --git a/MyMusicClientSveltePwa/src/lib/components/Song.svelte b/MyMusicClientSveltePwa/src/lib/components/Song.svelte index af52c19..2b81c0e 100644 --- a/MyMusicClientSveltePwa/src/lib/components/Song.svelte +++ b/MyMusicClientSveltePwa/src/lib/components/Song.svelte @@ -5,6 +5,12 @@ import { currentSong, isPlaying, isLoading } from "../scripts/playbackService"; import { getImageUrl, deleteSongFromPlaylist } from "../scripts/api"; import { removeSongFromPlaylist } from "../scripts/playlistService"; + import { getCurrentPlaylistId, setCurrentSongIndex, setCurrentSongTime } from "../scripts/storageService"; + import { get } from "svelte/store"; + + $: $isPlaying; + $: $currentSong; + $: $isLoading; export let song; export let playlistId; @@ -16,20 +22,27 @@ }); async function deleteSong() { - if(confirm(`Are you sure you want to delete the song "${song.name}" from this playlist?`)) { + if (confirm(`Are you sure you want to delete the song "${song.name}" from this playlist?`)) { var deleted = await deleteSongFromPlaylist(playlistId, song.id); - if(deleted) { + if (deleted) { removeSongFromPlaylist(song.id); + const currentPlaylistId = getCurrentPlaylistId(); + // If the deleted playlist is the current playing playlist, stop playback + const _currentSong = get(currentSong); + if (song.id === _currentSong.id) { + // stop playback + playOrPauseSong(null); + currentSong.set({ id: -999, title: "", artist: "", album: "", source_id: "" }); + setCurrentSongTime(0); + setCurrentSongIndex(-1); + } } else { alert(`Failed to delete song with ID: ${song.id} from playlist ID: ${playlistId}`); } } } - $: $isPlaying; - $: $currentSong; - $: $isLoading; {#if song} @@ -74,8 +87,7 @@ background-size: cover; background-position: center; overflow: hidden; - transition: - transform 0.2s ease, + transition: transform 0.2s ease; } .song:hover { @@ -110,7 +122,7 @@ .blur { position: absolute; inset: 0; - background-color: rgba(0, 0, 0, 0.60); + background-color: rgba(0, 0, 0, 0.6); backdrop-filter: blur(5px); z-index: 1; border-radius: 5px; diff --git a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte index 986e686..e5a3afd 100644 --- a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte +++ b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte @@ -6,7 +6,7 @@ import { playOrPauseSong, setPlaylists, updateCurrentPlaylist } from "../scripts/playbackService"; import SongComponent from "../components/Song.svelte"; - const updateIntervalTimeOut = 1500; // Update every second + const updateIntervalTimeOutInMs = 750; // Update every 750 ms let intervalId let updating = false @@ -19,13 +19,12 @@ setContext("playOrPauseSong", playOrPause); intervalId = setInterval(() => { - - if (updating) return; // Prevent multiple updates at the same time + // if (updating) return; // Prevent multiple updates at the same time updating = true; songs.set(getCachedPlaylistSongs(playlistId)); updateCurrentPlaylist(playlistId); updating = false; - }, updateIntervalTimeOut); + }, updateIntervalTimeOutInMs); }); function playOrPause(songId) { diff --git a/MyMusicClientSveltePwa/src/lib/scripts/playbackService.js b/MyMusicClientSveltePwa/src/lib/scripts/playbackService.js index ba40c32..2b76951 100644 --- a/MyMusicClientSveltePwa/src/lib/scripts/playbackService.js +++ b/MyMusicClientSveltePwa/src/lib/scripts/playbackService.js @@ -125,6 +125,12 @@ export function previousSong() { } export function playOrPauseSong(songId) { + + if (songId < 0) { + // Invalid songId, do nothing + return; + } + const _currentSong = get(currentSong); if (songId === null || songId === undefined) { diff --git a/MyMusicClientSveltePwa/src/lib/scripts/playlistService.js b/MyMusicClientSveltePwa/src/lib/scripts/playlistService.js index 5caa067..ec2977c 100644 --- a/MyMusicClientSveltePwa/src/lib/scripts/playlistService.js +++ b/MyMusicClientSveltePwa/src/lib/scripts/playlistService.js @@ -1,5 +1,5 @@ import { writable, get } from "svelte/store"; -import { getCachedPlaylists, setCachedPlaylists, setPlaylistSongs, getCachedPlaylistSongs, appConfiguration, getConfiguration, getCurrentPlaylistId } from "./storageService"; +import { getCachedPlaylists, setCachedPlaylists, setCachedPlaylistSongs, getCachedPlaylistSongs, appConfiguration, getConfiguration, getCurrentPlaylistId } from "./storageService"; import { fetchPlaylists, fetchPlaylistSongs, fetchNewPlaylist, fetchNewPlaylistSongs, deletePlaylist } from "./api"; import { componentParams, navigateTo } from "./routeService"; import { playOrPauseSong, setPlaylists, currentSong, playPercentage, updateCurrentPlaylist } from "./playbackService"; @@ -27,7 +27,7 @@ export async function initializePlaylistService() { setCachedPlaylists(fetchedPlaylists); for (const playlist of fetchedPlaylists) { const songs = await fetchPlaylistSongs(playlist.id); - setPlaylistSongs(playlist.id, songs); + setCachedPlaylistSongs(playlist.id, songs); } } @@ -69,16 +69,16 @@ export async function deleteCurrentPlaylist() { // TODO delete resource from API, images etc const result = await deletePlaylist(playlistId); if (result.success) { - const currentPlaylist = getCurrentPlaylistId(); + const currentPlaylistId = getCurrentPlaylistId(); // If the deleted playlist is the current playing playlist, stop playback - if (currentPlaylist === playlistId) { + if (currentPlaylistId === playlistId) { // stop playback playOrPauseSong(null); setPlaylists(0); - currentSong.set({id: -999, title: "", artist: "", album: "", source_id: ""}); + currentSong.set({ id: -999, title: "", artist: "", album: "", source_id: "" }); } - + playPercentage.set(0); // Remove playlist from cached playlists @@ -100,11 +100,11 @@ export function removeSongFromPlaylist(songId) { const playlistId = playlist.id; const cachedSongs = getCachedPlaylistSongs(playlistId); const songIndex = cachedSongs.findIndex((s) => s.id === songId); - if (songIndex !== -1) { - cachedSongs.splice(songIndex, 1); - setPlaylistSongs(playlistId, cachedSongs); + const removed = cachedSongs.splice(songIndex, 1); + + if (removed.length > 0) { + setCachedPlaylistSongs(playlistId, cachedSongs); } - updateCurrentPlaylist(playlistId); } } @@ -128,10 +128,15 @@ async function backgroundUpdate() { const cachedSongs = getCachedPlaylistSongs(playlistId); const lastKnowSongPosition = cachedSongs.length; - const newSongs = await fetchNewPlaylistSongs(playlistId, lastKnowSongPosition); - if (newSongs.length > 0) { - const updatedSongs = [...cachedSongs, ...newSongs]; - setPlaylistSongs(playlistId, updatedSongs); - } + const newPlaylistSongs = await fetchNewPlaylistSongs(playlistId, lastKnowSongPosition); + + // get the difference between cachedSongs and newPlaylistSongs + const songsToAdd = newPlaylistSongs.filter(song => !cachedSongs.some(cs => cs.id === song.id)); + + if (songsToAdd.length > 0) { + const updatedSongs = [...cachedSongs, ...songsToAdd]; + setCachedPlaylistSongs(playlistId, updatedSongs); + } + } } diff --git a/MyMusicClientSveltePwa/src/lib/scripts/storageService.js b/MyMusicClientSveltePwa/src/lib/scripts/storageService.js index 036fff9..0e390a5 100644 --- a/MyMusicClientSveltePwa/src/lib/scripts/storageService.js +++ b/MyMusicClientSveltePwa/src/lib/scripts/storageService.js @@ -22,7 +22,7 @@ export function setCachedPlaylists(playlists) { setItem(PlaylistsKey, playlists); } -export function setPlaylistSongs(playlistId, songs) { +export function setCachedPlaylistSongs(playlistId, songs) { const key = `${PlaylistSongsKey}${playlistId}`; setItem(key, songs); } From f247e84258338c9f575e9a24b565f20d559394b1 Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Sat, 18 Oct 2025 23:04:14 +0000 Subject: [PATCH 04/17] update video_archive --- MyMusicBoxApi/http/playlistsong.go | 47 ++++++++++++++++++++++++++++++ MyMusicBoxApi/service/ytdlp.go | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/MyMusicBoxApi/http/playlistsong.go b/MyMusicBoxApi/http/playlistsong.go index e2bc995..6f84222 100644 --- a/MyMusicBoxApi/http/playlistsong.go +++ b/MyMusicBoxApi/http/playlistsong.go @@ -1,12 +1,15 @@ package http import ( + "bufio" "fmt" "musicboxapi/configuration" "musicboxapi/database" "musicboxapi/models" "net/http" + "os" "strconv" + "strings" "github.com/gin-gonic/gin" ) @@ -124,6 +127,50 @@ func (handler *PlaylistSongHandler) DeletePlaylistSong(ctx *gin.Context) { // Delete from database songTable.DeleteSongById(song.Id) + + // Delete from video_archive + strSplit := strings.Split(song.Path, ".") + + filenameWithOutExtension := strSplit[0] + strSplit = strings.Split(filenameWithOutExtension, "/") + + filenameWithOutExtension = strSplit[1] + + filePath := fmt.Sprintf("%s/%s", configuration.Config.SourceFolder, "video_archive") + + // Read the file + file, err := os.Open(filePath) + if err != nil { + fmt.Println("Error opening file:", err) + ctx.JSON(http.StatusInternalServerError, models.ErrorResponse(err)) + return + } + defer file.Close() + + var lines []string + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + // Keep the line only if it’s not the target + if strings.TrimSpace(line) != fmt.Sprintf("youtube %s", filenameWithOutExtension) { + lines = append(lines, line) + } + } + + if err := scanner.Err(); err != nil { + fmt.Println("Error reading file:", err) + ctx.JSON(http.StatusInternalServerError, models.ErrorResponse(err)) + return + } + + // Write the updated content back to the file + output := strings.Join(lines, "\n") + err = os.WriteFile(filePath, []byte(output+"\n"), 0644) + if err != nil { + fmt.Println("Error writing file:", err) + ctx.JSON(http.StatusInternalServerError, models.ErrorResponse(err)) + return + } } ctx.Status(http.StatusOK) diff --git a/MyMusicBoxApi/service/ytdlp.go b/MyMusicBoxApi/service/ytdlp.go index 212172c..415f5dc 100644 --- a/MyMusicBoxApi/service/ytdlp.go +++ b/MyMusicBoxApi/service/ytdlp.go @@ -26,7 +26,7 @@ func StartDownloadTask(downloadRequest models.DownloadRequestModel) { config := configuration.Config storageFolderName := config.SourceFolder - archiveFileName := fmt.Sprintf("%s/video_archive", storageFolderName) + archiveFileName := fmt.Sprintf("%s/video_archive", storageFolderName) // move to env / congif idsFileName := fmt.Sprintf("%s/ids.%d", storageFolderName, parentTask.Id) namesFileName := fmt.Sprintf("%s/names.%d", storageFolderName, parentTask.Id) durationFileName := fmt.Sprintf("%s/durations.%d", storageFolderName, parentTask.Id) From 29b246cb2c5df2ed13972908293184cc11ec8708 Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Sun, 19 Oct 2025 01:26:49 +0200 Subject: [PATCH 05/17] temp fix --- .../src/lib/pages/Playlists.svelte | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte index e5a3afd..d96676c 100644 --- a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte +++ b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte @@ -11,6 +11,11 @@ let updating = false export let playlistId = -1; + let visibleCount = 100; + + function loadMore() { + visibleCount += 100; + } let songs = writable([]); @@ -39,11 +44,16 @@ {#if $songs.length > 0}
- {#each $songs as song} + {#each $songs.slice(0, visibleCount) as song}
{/each} + {#if visibleCount < $songs.length} +
+ +
+ {/if}
{:else}

No songs in playlist.

From 5bb51bd6e0571bf0e76be831fc3c1596f1a09436 Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Tue, 21 Oct 2025 20:02:29 +0200 Subject: [PATCH 06/17] log error for not updating ui --- MyMusicClientSveltePwa/src/lib/scripts/api.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MyMusicClientSveltePwa/src/lib/scripts/api.js b/MyMusicClientSveltePwa/src/lib/scripts/api.js index 3b81383..84c5571 100644 --- a/MyMusicClientSveltePwa/src/lib/scripts/api.js +++ b/MyMusicClientSveltePwa/src/lib/scripts/api.js @@ -92,6 +92,11 @@ export async function deletePlaylist(playlistId) { }); if (!response.ok) { + + response.json().then((data) => { + console.error("Error deleting playlist:", data); + }); + throw new Error(`HTTP error! status: ${response}`); } return { From e2b07e58eb7abaaf5454141363c235642db5b2d8 Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Tue, 21 Oct 2025 18:27:47 +0000 Subject: [PATCH 07/17] wraaaaaa --- MyMusicBoxApi/database/db.go | 2 +- MyMusicBoxApi/database/playlistsongtable.go | 4 ++-- MyMusicBoxApi/http/playlistsong.go | 3 ++- MyMusicBoxApi/http/playlistsong_test.go | 2 +- MyMusicBoxApi/models/http.go | 1 + 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/MyMusicBoxApi/database/db.go b/MyMusicBoxApi/database/db.go index aafc63a..f04fd6b 100644 --- a/MyMusicBoxApi/database/db.go +++ b/MyMusicBoxApi/database/db.go @@ -45,7 +45,7 @@ func CreateDatabasConnectionPool() error { // Should create test for these? var _ ISongTable = (*SongTable)(nil) var _ IPlaylistTable = (*PlaylistTable)(nil) - var _ IPlaylistsongTable = (*PlaylistsongTable)(nil) + var _ IPlaylistSongTable = (*PlaylistsongTable)(nil) var _ ITasklogTable = (*TasklogTable)(nil) var _ IMigrationTable = (*MigrationTable)(nil) diff --git a/MyMusicBoxApi/database/playlistsongtable.go b/MyMusicBoxApi/database/playlistsongtable.go index 65cff61..02323e6 100644 --- a/MyMusicBoxApi/database/playlistsongtable.go +++ b/MyMusicBoxApi/database/playlistsongtable.go @@ -7,7 +7,7 @@ import ( "musicboxapi/models" ) -type IPlaylistsongTable interface { +type IPlaylistSongTable interface { FetchPlaylistSongs(ctx context.Context, playlistId int, lastKnowPosition int) (songs []models.Song, error error) InsertPlaylistSong(playlistId int, songId int) (lastInsertedId int, error error) DeleteAllPlaylistSongs(playlistId int) (error error) @@ -18,7 +18,7 @@ type PlaylistsongTable struct { BaseTable } -func NewPlaylistsongTableInstance() IPlaylistsongTable { +func NewPlaylistsongTableInstance() IPlaylistSongTable { return &PlaylistsongTable{ BaseTable: NewBaseTableInstance(), } diff --git a/MyMusicBoxApi/http/playlistsong.go b/MyMusicBoxApi/http/playlistsong.go index 6f84222..870e135 100644 --- a/MyMusicBoxApi/http/playlistsong.go +++ b/MyMusicBoxApi/http/playlistsong.go @@ -15,7 +15,7 @@ import ( ) type PlaylistSongHandler struct { - PlaylistsongTable database.IPlaylistsongTable + PlaylistsongTable database.IPlaylistSongTable } const DefaultPlaylistId = 1 @@ -106,6 +106,7 @@ func (handler *PlaylistSongHandler) DeletePlaylistSong(ctx *gin.Context) { return } + // Thumbnail and .opus file will be deleted only if you delete a song via the main playlist containing all the songs if playlistId == DefaultPlaylistId { songTable := database.NewSongTableInstance() diff --git a/MyMusicBoxApi/http/playlistsong_test.go b/MyMusicBoxApi/http/playlistsong_test.go index ab9c3b4..679ae54 100644 --- a/MyMusicBoxApi/http/playlistsong_test.go +++ b/MyMusicBoxApi/http/playlistsong_test.go @@ -13,7 +13,7 @@ import ( ) type mockPlaylistSongTable struct { - database.IPlaylistsongTable + database.IPlaylistSongTable fetchPlaylistSongs func(ctx context.Context, playlistId int, lastKnowPosition int) (songs []models.Song, error error) insertPlaylistSong func(playlistId int, songId int) (lastInsertedId int, error error) deleteAllPlaylistSongs func(playlistId int) (error error) // TODO diff --git a/MyMusicBoxApi/models/http.go b/MyMusicBoxApi/models/http.go index e62b8c2..9b5aa73 100644 --- a/MyMusicBoxApi/models/http.go +++ b/MyMusicBoxApi/models/http.go @@ -19,6 +19,7 @@ type ApiResponseModel struct { } func ErrorResponse(data any) ApiResponseModel { + return ApiResponseModel{ Data: data, Message: "An error occurred", From 21f9c524488724921cff8897cc82ad7af3b00fcf Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Tue, 21 Oct 2025 18:28:20 +0000 Subject: [PATCH 08/17] no passing solid wall thing block --- MyMusicBoxApi/database/playlisttable.go | 8 ++++---- MyMusicBoxApi/http/playlist.go | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/MyMusicBoxApi/database/playlisttable.go b/MyMusicBoxApi/database/playlisttable.go index 55acb11..12a8e71 100644 --- a/MyMusicBoxApi/database/playlisttable.go +++ b/MyMusicBoxApi/database/playlisttable.go @@ -24,7 +24,7 @@ func NewPlaylistTableInstance() IPlaylistTable { } func (table *PlaylistTable) FetchPlaylists(ctx context.Context, lastKnowPlaylistId int) (playlists []models.Playlist, error error) { - query := "SELECT Id, Name, ThumbnailPath, Description, CreationDate FROM Playlist WHERE Id > $1 ORDER BY Id" // order by? + query := "SELECT Id, Name, ThumbnailPath, Description, CreationDate, IsPublic FROM Playlist WHERE Id > $1 ORDER BY Id" // order by? rows, err := table.QueryRowsContex(ctx, query, lastKnowPlaylistId) @@ -41,7 +41,7 @@ func (table *PlaylistTable) FetchPlaylists(ctx context.Context, lastKnowPlaylist playlists = make([]models.Playlist, 0) for rows.Next() { - scanError := rows.Scan(&playlist.Id, &playlist.Name, &playlist.ThumbnailPath, &playlist.Description, &playlist.CreationDate) + scanError := rows.Scan(&playlist.Id, &playlist.Name, &playlist.ThumbnailPath, &playlist.Description, &playlist.CreationDate, &playlist.IsPublic) if scanError != nil { logging.Error(fmt.Sprintf("Scan error: %s", scanError.Error())) @@ -68,9 +68,9 @@ func (table *PlaylistTable) InsertPlaylist(playlist models.Playlist) (lastInsert } func (table *PlaylistTable) DeletePlaylist(playlistId int) (error error) { - query := `DELETE FROM Playlist WHERE Id = $1` + query := `DELETE FROM Playlist WHERE Id = $1 AND IsPublic = $2` // Prevemts private playlists (like the default one) from being deleted for real - err := table.NonScalarQuery(query, playlistId) + err := table.NonScalarQuery(query, playlistId, true) if err != nil { logging.Error(fmt.Sprintf("Failed to delete playlist: %s", err.Error())) diff --git a/MyMusicBoxApi/http/playlist.go b/MyMusicBoxApi/http/playlist.go index 48b9f43..69e7ce9 100644 --- a/MyMusicBoxApi/http/playlist.go +++ b/MyMusicBoxApi/http/playlist.go @@ -110,6 +110,11 @@ func (handler *PlaylistHandler) DeletePlaylist(ctx *gin.Context) { return } + if DefaultPlaylistId == id { + ctx.JSON(http.StatusInternalServerError, models.ErrorResponse("Funky music... ")) + return + } + err = handler.PlaylistTable.DeletePlaylist(id) // TODO delete background image if its not the default image for it From f82a731987b0f88aba0fdcd5967a80e56542242f Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Wed, 22 Oct 2025 20:23:47 +0200 Subject: [PATCH 09/17] simba --- MyMusicClientSveltePwa/package-lock.json | 7 +++ MyMusicClientSveltePwa/package.json | 1 + .../src/lib/pages/Playlists.svelte | 47 ++++++++++++------- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/MyMusicClientSveltePwa/package-lock.json b/MyMusicClientSveltePwa/package-lock.json index 6f1d14a..3d4884b 100644 --- a/MyMusicClientSveltePwa/package-lock.json +++ b/MyMusicClientSveltePwa/package-lock.json @@ -8,6 +8,7 @@ "name": "mymusicclientsveltepwa", "version": "0.1.11", "dependencies": { + "@sveltejs/svelte-virtual-list": "^3.0.1", "@sveltestrap/sveltestrap": "^7.1.0" }, "devDependencies": { @@ -2422,6 +2423,12 @@ "acorn": "^8.9.0" } }, + "node_modules/@sveltejs/svelte-virtual-list": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/svelte-virtual-list/-/svelte-virtual-list-3.0.1.tgz", + "integrity": "sha512-aF9TptS7NKKS7/TqpsxQBSDJ9Q0XBYzBehCeIC5DzdMEgrJZpIYao9LRLnyyo6SVodpapm2B7FE/Lj+FSA5/SQ==", + "license": "LIL" + }, "node_modules/@sveltejs/vite-plugin-svelte": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.1.0.tgz", diff --git a/MyMusicClientSveltePwa/package.json b/MyMusicClientSveltePwa/package.json index 88d5e0a..c4c8bf0 100644 --- a/MyMusicClientSveltePwa/package.json +++ b/MyMusicClientSveltePwa/package.json @@ -18,6 +18,7 @@ "vite-plugin-pwa": "^1.0.0" }, "dependencies": { + "@sveltejs/svelte-virtual-list": "^3.0.1", "@sveltestrap/sveltestrap": "^7.1.0" } } diff --git a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte index d96676c..4ef4605 100644 --- a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte +++ b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte @@ -1,14 +1,20 @@ -{#if $songs.length > 0} -
- {#each $songs.slice(0, visibleCount) as song} -
- -
- {/each} - {#if visibleCount < $songs.length} -
- -
- {/if} -
+{#if readableSongs.length > 0} + +
+ + + +
{:else}

No songs in playlist.

{/if} + + From 35296921a119eb30347fe790a50f966c2fbf8cc0 Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Wed, 22 Oct 2025 20:24:58 +0200 Subject: [PATCH 10/17] s --- MyMusicClientSveltePwa/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MyMusicClientSveltePwa/package.json b/MyMusicClientSveltePwa/package.json index c4c8bf0..5815fb6 100644 --- a/MyMusicClientSveltePwa/package.json +++ b/MyMusicClientSveltePwa/package.json @@ -1,7 +1,7 @@ { "name": "mymusicclientsveltepwa", "private": true, - "version": "0.1.11", + "version": "0.1.11-beta", "type": "module", "scripts": { "dev": "vite --host", From 2786fca95428f6a0cb4cd04c24dac9cf4a23261e Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Wed, 22 Oct 2025 20:26:25 +0200 Subject: [PATCH 11/17] sssssss --- MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte index 4ef4605..1a9302b 100644 --- a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte +++ b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte @@ -6,7 +6,6 @@ import { playOrPauseSong, setPlaylists, updateCurrentPlaylist } from "../scripts/playbackService"; import SongComponent from "../components/Song.svelte"; import VirtualList from "@sveltejs/svelte-virtual-list/VirtualList.svelte"; - import items from "../pages/data"; import ListItem from "../components/ListItem.svelte"; import Song from "../components/Song.svelte"; From 2ef24c63f5c03337fc306125f70bc47edcf33688 Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Wed, 22 Oct 2025 20:27:34 +0200 Subject: [PATCH 12/17] I saw this --- MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte index 1a9302b..dd20ac1 100644 --- a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte +++ b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte @@ -6,7 +6,6 @@ import { playOrPauseSong, setPlaylists, updateCurrentPlaylist } from "../scripts/playbackService"; import SongComponent from "../components/Song.svelte"; import VirtualList from "@sveltejs/svelte-virtual-list/VirtualList.svelte"; - import ListItem from "../components/ListItem.svelte"; import Song from "../components/Song.svelte"; const updateIntervalTimeOutInMs = 750; // Update every 750 ms From cf1ffbdf0903235be2b6108a5cd86a56a60d5558 Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Thu, 23 Oct 2025 16:22:29 +0000 Subject: [PATCH 13/17] fix test --- MyMusicBoxApi/http/playlist_test.go | 33 +++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/MyMusicBoxApi/http/playlist_test.go b/MyMusicBoxApi/http/playlist_test.go index eef14d5..4e08d17 100644 --- a/MyMusicBoxApi/http/playlist_test.go +++ b/MyMusicBoxApi/http/playlist_test.go @@ -265,8 +265,7 @@ func TestDeletePlaylistPlaylistId(t *testing.T) { recorder := httptest.NewRecorder() - // Unable to parse to int, will throw error - _route := "/playlist/1" + _route := "/playlist/2" req, _ := http.NewRequest("DELETE", _route, nil) @@ -276,3 +275,33 @@ func TestDeletePlaylistPlaylistId(t *testing.T) { // Assert assert.Equal(t, http.StatusOK, recorder.Code) } + +func TestDeletePlaylistPlaylistIdDefaultPlaylist(t *testing.T) { + // Arrange + route := "/playlist/:playlistId" + router := SetupTestRouter() + + mockTable := &mockPlaylistTable{ + deletePlaylist: func(playlistId int) (error error) { + return nil + }, + } + + playlistHandler := PlaylistHandler{ + PlaylistTable: mockTable, + } + + router.DELETE(route, playlistHandler.DeletePlaylist) + + recorder := httptest.NewRecorder() + + _route := "/playlist/1" + + req, _ := http.NewRequest("DELETE", _route, nil) + + // Act + router.ServeHTTP(recorder, req) + + // Assert + assert.Equal(t, http.StatusInternalServerError, recorder.Code) +} From 5eb4f582f287c8266a35cc0faeca38b8042e2500 Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Thu, 23 Oct 2025 16:22:36 +0000 Subject: [PATCH 14/17] Meh --- MyMusicBoxApi/models/http.go | 1 + 1 file changed, 1 insertion(+) diff --git a/MyMusicBoxApi/models/http.go b/MyMusicBoxApi/models/http.go index 9b5aa73..0f440eb 100644 --- a/MyMusicBoxApi/models/http.go +++ b/MyMusicBoxApi/models/http.go @@ -25,6 +25,7 @@ func ErrorResponse(data any) ApiResponseModel { Message: "An error occurred", } } + func OkResponse(data any, message string) ApiResponseModel { return ApiResponseModel{ Data: data, From 5b82bcd4b215ba9f46d53c6d325661659021ae9e Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Sat, 25 Oct 2025 14:14:25 +0200 Subject: [PATCH 15/17] play bug fix, ui fixes --- .../src/lib/pages/Playlists.svelte | 13 ++++++++----- .../src/lib/scripts/playbackService.js | 1 - 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte index dd20ac1..87df83a 100644 --- a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte +++ b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte @@ -30,7 +30,7 @@ readableSongs = getCachedPlaylistSongs(playlistId); intervalId = setInterval(() => { - // if (updating) return; // Prevent multiple updates at the same time + if (updating) return; // Prevent multiple updates at the same time updating = true; readableSongs = getCachedPlaylistSongs(playlistId); songs.set(readableSongs); @@ -41,6 +41,7 @@ function playOrPause(songId) { setPlaylists(playlistId); + playOrPauseSong(songId); } onDestroy(() => { @@ -50,7 +51,7 @@ {#if readableSongs.length > 0} -
+
@@ -61,9 +62,11 @@ diff --git a/MyMusicClientSveltePwa/src/lib/scripts/playbackService.js b/MyMusicClientSveltePwa/src/lib/scripts/playbackService.js index 2b76951..955c552 100644 --- a/MyMusicClientSveltePwa/src/lib/scripts/playbackService.js +++ b/MyMusicClientSveltePwa/src/lib/scripts/playbackService.js @@ -125,7 +125,6 @@ export function previousSong() { } export function playOrPauseSong(songId) { - if (songId < 0) { // Invalid songId, do nothing return; From d060f8c6fb85819dd866924e1b2afa96b2c8f810 Mon Sep 17 00:00:00 2001 From: DutchJavaDev Date: Wed, 5 Nov 2025 00:15:52 +0100 Subject: [PATCH 16/17] block scroll content --- MyMusicClientSveltePwa/src/App.svelte | 37 ++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/MyMusicClientSveltePwa/src/App.svelte b/MyMusicClientSveltePwa/src/App.svelte index 823cc33..b497fed 100644 --- a/MyMusicClientSveltePwa/src/App.svelte +++ b/MyMusicClientSveltePwa/src/App.svelte @@ -2,8 +2,8 @@ -
+
 
-
+
-
+
+
+
+
{:else}

No song available.

@@ -79,22 +98,22 @@ position: relative; background: #2c2c2c; border-radius: 10px; - padding: 0px 10px; margin-bottom: 10px; min-height: 80px; - box-shadow: 0 4px 12px rgba(90, 89, 89, 0.4); background-image: var(--url); background-size: cover; background-position: center; overflow: hidden; transition: transform 0.2s ease; + border-radius: 2px; + margin: auto; } .song:hover { transform: translateY(-3px); } - .song-info { + /* .song-info { flex: 1; margin: 0 12px; display: flex; @@ -105,15 +124,14 @@ -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis; - } + } */ .title { - color: #ffffff; - font-weight: bold; - font-size: 0.5rem; - margin: 5px; + color: #b3b3b3; + font-size: 0.7rem; + margin-top: 5px; display: -webkit-box; - -webkit-line-clamp: 2; - line-clamp: 2; + -webkit-line-clamp: 3; + line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis; @@ -122,10 +140,10 @@ .blur { position: absolute; inset: 0; - background-color: rgba(0, 0, 0, 0.6); - backdrop-filter: blur(5px); + background-color: #2c2c2c76; + backdrop-filter: blur(10px); z-index: 1; - border-radius: 5px; + /* border-radius: 5px; */ } /* Keep content visible above blur */ @@ -133,4 +151,63 @@ position: relative; z-index: 2; } + +@keyframes lights { + 0% { + color: #1DB954; + text-shadow: + 0 0 0.35em hsla(140, 80%, 50%, 0.25), + 0 0 0.1em hsla(140, 80%, 60%, 0.2); + } + + 30% { + color: #1db954b5; + text-shadow: + 0 0 0.45em hsla(140, 80%, 50%, 0.35), + 0 0 0.15em hsla(140, 80%, 60%, 0.25), + -0.15em -0.05em 0.15em hsla(60, 90%, 60%, 0.15), + 0.15em 0.05em 0.15em hsla(200, 100%, 60%, 0.2); + } + + 40% { + color: #1db9549d; + text-shadow: + 0 0 0.4em hsla(140, 80%, 50%, 0.3), + 0 0 0.12em hsla(140, 80%, 80%, 0.25), + -0.1em -0.05em 0.1em hsla(60, 90%, 60%, 0.12), + 0.1em 0.05em 0.1em hsla(200, 100%, 60%, 0.15); + } + + 70% { + color: #1db95464; + text-shadow: + 0 0 0.35em hsla(140, 80%, 50%, 0.25), + 0 0 0.1em hsla(140, 80%, 60%, 0.2), + 0.15em -0.05em 0.12em hsla(60, 90%, 60%, 0.12), + -0.15em 0.05em 0.12em hsla(200, 100%, 60%, 0.18); + } + + 100% { + color: #1DB954; + text-shadow: + 0 0 0.35em hsla(140, 80%, 50%, 0.25), + 0 0 0.1em hsla(140, 80%, 60%, 0.2); + } +} + +/* body { + margin: 0; + font: 100% / 1.5 Raleway, sans-serif; + color: hsl(230, 100%, 95%); + background: linear-gradient(135deg, hsl(230, 40%, 12%), hsl(230, 20%, 7%)); + height: 100vh; + display: flex; +} */ + +.iamcute { + /* margin: auto; + font-size: 3.5rem; + font-weight: 300; */ + animation: lights 2s 500ms linear infinite; +} diff --git a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte index 87df83a..8814e3d 100644 --- a/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte +++ b/MyMusicClientSveltePwa/src/lib/pages/Playlists.svelte @@ -1,7 +1,7 @@