Skip to content

Commit 40e94c7

Browse files
Frontend/create a playlists endpoint exist in api (#84)
* Action button / open create playlist modal * playlist modal * create playlist backend * form data playlist model * update playlist insert * update insert playlist endpoint * guid package * fix image file name * alter me * chore: bump version --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 88e89e4 commit 40e94c7

File tree

10 files changed

+188
-39
lines changed

10 files changed

+188
-39
lines changed

MyMusicBoxApi/database/playlisttable.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,13 @@ func (table *PlaylistTable) FetchPlaylists(ctx context.Context, lastKnowPlaylist
5555
}
5656

5757
func (table *PlaylistTable) InsertPlaylist(playlist models.Playlist) (lastInsertedId int, error error) {
58-
query := `INSERT INTO Playlist (name, description, thumbnailPath) VALUES ($1, $2, $3) RETURNING Id`
58+
query := `INSERT INTO Playlist (name, description, thumbnailPath, ispublic) VALUES ($1, $2, $3, $4) RETURNING Id`
5959

6060
lastInsertedId, err := table.InsertWithReturningId(query,
6161
playlist.Name,
6262
playlist.Description,
6363
playlist.ThumbnailPath,
64+
playlist.IsPublic,
6465
)
6566

6667
return lastInsertedId, err

MyMusicBoxApi/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ require (
2929
github.com/go-openapi/swag/stringutils v0.24.0 // indirect
3030
github.com/go-openapi/swag/typeutils v0.24.0 // indirect
3131
github.com/go-openapi/swag/yamlutils v0.24.0 // indirect
32+
github.com/google/uuid v1.6.0 // indirect
3233
github.com/josharian/intern v1.0.0 // indirect
3334
github.com/mailru/easyjson v0.9.0 // indirect
3435
github.com/pmezard/go-difflib v1.0.0 // indirect

MyMusicBoxApi/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
6666
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
6767
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
6868
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
69+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
70+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
6971
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
7072
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
7173
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=

MyMusicBoxApi/http/playlist.go

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@ package http
22

33
import (
44
"fmt"
5+
"mime/multipart"
6+
"musicboxapi/configuration"
57
"musicboxapi/database"
8+
"musicboxapi/logging"
69
"musicboxapi/models"
710
"net/http"
11+
"path/filepath"
812
"strconv"
13+
"strings"
914

1015
"github.com/gin-gonic/gin"
16+
"github.com/google/uuid"
1117
)
1218

1319
type PlaylistHandler struct {
@@ -38,23 +44,66 @@ func (handler *PlaylistHandler) FetchPlaylists(ctx *gin.Context) {
3844
ctx.JSON(http.StatusOK, models.OkResponse(playlists, fmt.Sprintf("Found %d playlist", len(playlists))))
3945
}
4046

47+
type FormSt struct {
48+
Name string `form:"playlistName"`
49+
Image multipart.FileHeader `form:"backgroundImage"`
50+
IsPublic string `form:"publicPlaylist"`
51+
Description string `form:"playlistDescription"`
52+
}
53+
4154
func (hanlder *PlaylistHandler) InsertPlaylist(ctx *gin.Context) {
42-
var playlist models.Playlist
4355

44-
err := ctx.ShouldBindBodyWithJSON(&playlist)
56+
var playlistModel models.CreatePlaylistModel
57+
58+
err := ctx.ShouldBind(&playlistModel)
4559

4660
if err != nil {
61+
logging.ErrorStackTrace(err)
4762
ctx.JSON(http.StatusInternalServerError, models.ErrorResponse(err))
4863
return
4964
}
5065

66+
var playlist models.Playlist
67+
var fileName string
68+
69+
hasFormFile := playlistModel.Image.Size > 0
70+
71+
if hasFormFile {
72+
fileName = fmt.Sprintf("%s.jpg", uuid.New().String())
73+
playlist.ThumbnailPath = fileName
74+
} else {
75+
// default_playlist_cover.jpg
76+
playlist.ThumbnailPath = "default_playlist_cover.jpg"
77+
}
78+
79+
playlist.Name = playlistModel.Name
80+
playlist.Description = playlistModel.Description
81+
82+
if strings.Contains(playlistModel.IsPublic, "on") {
83+
playlist.IsPublic = true
84+
}
85+
5186
playlistId, err := hanlder.PlaylistTable.InsertPlaylist(playlist)
5287

5388
if err != nil {
5489
ctx.JSON(http.StatusInternalServerError, models.ErrorResponse(err))
5590
return
5691
}
5792

93+
if hasFormFile {
94+
path := filepath.Join(configuration.Config.SourceFolder, fmt.Sprintf("images/%s", fileName))
95+
96+
logging.Info(path)
97+
98+
err = ctx.SaveUploadedFile(playlistModel.Image, path)
99+
100+
if err != nil {
101+
logging.ErrorStackTrace(err)
102+
ctx.JSON(http.StatusInternalServerError, models.ErrorResponse(err))
103+
return
104+
}
105+
}
106+
58107
ctx.JSON(http.StatusOK, models.OkResponse(gin.H{"playlistId": playlistId}, "Created new playlist"))
59108
}
60109

MyMusicBoxApi/models/http.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
package models
22

3+
import "mime/multipart"
4+
35
type DownloadRequestModel struct {
46
Url string `json:"url"`
57
}
68

9+
type CreatePlaylistModel struct {
10+
Name string `form:"playlistName"`
11+
Image *multipart.FileHeader `form:"backgroundImage"`
12+
IsPublic string `form:"publicPlaylist"`
13+
Description string `form:"playlistDescription"`
14+
}
15+
716
type ApiResponseModel struct {
817
Data any
918
Message string

MyMusicClientSveltePwa/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

MyMusicClientSveltePwa/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "mymusicclientsveltepwa",
33
"private": true,
4-
"version": "0.1.10",
4+
"version": "0.1.11",
55
"type": "module",
66
"scripts": {
77
"dev": "vite --host",

MyMusicClientSveltePwa/src/App.svelte

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@
6767
<div class="col-3 col-lg-2 col-md-2 col-sm-2">
6868
<button aria-label="home" class="btn btn-dark w-100" on:click={() => navigateTo("/Home")}><i class="fa-solid fa-house"></i></button>
6969
</div>
70+
<div class="col-3 col-lg-2 col-md-2 col-sm-2">
71+
<div class="btn-group dropup w-100">
72+
73+
<button type="button" aria-label="home" class="btn btn-dark w-100 dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><i class="fa-solid fa-plus"></i></button>
74+
75+
<div class="dropdown-menu bg-dark w-100">
76+
<button class="btn btn-primary dropdown-item" data-bs-toggle="modal" data-bs-target="#createPlaylistModal" >New Playlist</button>
77+
</div>
78+
</div>
79+
</div>
7080
<div class="col-3 col-lg-2 col-md-2 col-sm-2">
7181
<button aria-label="home" class="btn btn-dark w-100" on:click={() => navigateTo("/Settings")}><i class="fa-solid fa-gear"></i></button>
7282
</div>
@@ -121,5 +131,6 @@
121131
.bottom-bar button {
122132
font-weight: bolder;
123133
background-color: #2c2c2c !important;
134+
color: white;
124135
}
125136
</style>

MyMusicClientSveltePwa/src/lib/components/Modals.svelte

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
<script>
2-
// @ts-nocheck
3-
4-
import { isPlaying, currentSong,
5-
playPercentage, setCurrentTime,
6-
nextSong, previousSong,
7-
isShuffledEnabled, isLoopingEnabled,
8-
toggleShuffle, playOrPauseSong,
9-
toggleLoop, isLoading } from "../scripts/playbackService";
10-
import { getImageUrl } from "../scripts/api";
2+
// @ts-nocheck
3+
4+
import { isPlaying, currentSong, playPercentage, setCurrentTime, nextSong, previousSong, isShuffledEnabled, isLoopingEnabled, toggleShuffle, playOrPauseSong, toggleLoop, isLoading } from "../scripts/playbackService";
5+
import { getImageUrl, createPlaylist } from "../scripts/api";
116
import { get } from "svelte/store";
127
import { isTimerEnabled, timeLeft, toggleSleepTimer } from "../scripts/sleeptimerService";
138
@@ -18,8 +13,8 @@
1813
$: $isLoopingEnabled;
1914
$: $isTimerEnabled;
2015
$: $timeLeft;
21-
$: $isLoading
22-
16+
$: $isLoading;
17+
2318
function togglePlay() {
2419
playOrPauseSong(get(currentSong).id);
2520
}
@@ -35,10 +30,32 @@
3530
setCurrentTime(newTime);
3631
}
3732
}
33+
34+
async function handleCreatePlaylistSubmit(e) {
35+
event.preventDefault();
36+
const formData = new FormData(event.target);
37+
var response = await createPlaylist(formData);
38+
39+
if (response.success){
40+
alert(`Playlist has been created successfully.`);
41+
42+
// Close modal
43+
const modalElement = document.getElementById('createPlaylistModal');
44+
const modalInstance = bootstrap.Modal.getInstance(modalElement);
45+
modalInstance.hide();
46+
47+
// clear form
48+
event.target.reset();
49+
50+
} else {
51+
alert(`Failed to create playlist: ${response.data}`);
52+
}
53+
}
3854
</script>
3955

40-
<!-- Modal -->
41-
{#if $currentSong && $currentSong.id !== -999} <!-- Ensure currentSong is valid -->
56+
<!-- Play Modal -->
57+
{#if $currentSong && $currentSong.id !== -999}
58+
<!-- Ensure currentSong is valid -->
4259
<div class="modal fade" id="songControlModal" tabindex="-1" aria-labelledby="songControlModalLabel" aria-hidden="false">
4360
<div class="modal-dialog modal-fullscreen-sm-down">
4461
<div class="modal-content">
@@ -47,7 +64,7 @@
4764
<div class="row">
4865
<div class="col-12">
4966
<div class="container-fluid" style="height: 7rem;">
50-
<p class="text-white" id="songControlModalLabel">{$currentSong.name}</p>
67+
<p class="text-white" id="songControlModalLabel">{$currentSong.name}</p>
5168
</div>
5269
</div>
5370
<div class="col-12 text-center">
@@ -88,22 +105,22 @@
88105
<div class="row mt-5">
89106
<div class="col-4">
90107
<button on:click={toggleSleepTimer} aria-label="sleep timer" type="button" class="btn w-100">
91-
<i class="fa-solid fa-stopwatch-20" style="{$isTimerEnabled ? "color: #1CC558;" : "color:#ACACAC;"}">
108+
<i class="fa-solid fa-stopwatch-20" style={$isTimerEnabled ? "color: #1CC558;" : "color:#ACACAC;"}>
92109
<span style="font-size: 0.8rem;">
93-
&nbsp;{$isTimerEnabled ? $timeLeft : ""}
94-
</span>
95-
</i>
110+
&nbsp;{$isTimerEnabled ? $timeLeft : ""}
111+
</span>
112+
</i>
96113
</button>
97114
</div>
98115

99116
<div class="col-4">
100117
<button on:click={toggleShuffle} aria-label="shuffle playlist" type="button" class="btn w-100">
101-
<i class="fa-solid fa-shuffle" style="{$isShuffledEnabled ? "color: #1CC558;" : "color:#ACACAC;"}"></i>
118+
<i class="fa-solid fa-shuffle" style={$isShuffledEnabled ? "color: #1CC558;" : "color:#ACACAC;"}></i>
102119
</button>
103120
</div>
104121
<div class="col-4">
105122
<button on:click={toggleLoop} aria-label="repeat song" type="button" class="btn w-100">
106-
<i class="fa-solid fa-repeat" style="{$isLoopingEnabled ? "color: #1CC558;" : "color:#ACACAC;"}"></i>
123+
<i class="fa-solid fa-repeat" style={$isLoopingEnabled ? "color: #1CC558;" : "color:#ACACAC;"}></i>
107124
</button>
108125
</div>
109126
</div>
@@ -119,45 +136,79 @@
119136
</div>
120137
{/if}
121138

139+
<!-- Create Playlist Modal -->
140+
<div class="modal fade" id="createPlaylistModal" tabindex="-1" aria-labelledby="createPlaylistModalLabel" aria-hidden="false">
141+
<div class="modal-dialog modal-fullscreen-sm-down">
142+
<div class="modal-content">
143+
<div class="modal-body">
144+
<form on:submit|preventDefault={handleCreatePlaylistSubmit} class="p-2 rounded rounded-2 tile-bg">
145+
<div class="mb-3">
146+
<label for="playlistName" class="form-label">Playlist Name</label>
147+
<input type="text" required class="form-control form-control-sm" id="playlistName" name="playlistName" placeholder="Name of newly created playlist" />
148+
</div>
149+
<div class="mt-3 mb-3 p-2 rounded rounded-2 tile-bg">
150+
<label for="backgroundImage" class="form-label">Playlist Image (leave blank for default)</label>
151+
<input type="file" id="backgroundImage" name="backgroundImage" class="form-file-input form-control-sm" />
152+
</div>
153+
<div class="mt-3 mb-3 p-2 rounded rounded-2 tile-bg">
154+
<label for="publicPlaylist" class="form-label">Public</label>
155+
<input type="checkbox" checked id="publicPlaylist" name="publicPlaylist" class="form-check-input" />
156+
</div>
157+
<div class="mt-3 mb-3 p-2 rounded rounded-2 tile-bg">
158+
<label for="playlistDescription" class="form-label">Description</label>
159+
<textarea id="playlistDescription" name="playlistDescription" rows="3" class="form-control form-control-sm"> </textarea>
160+
</div>
161+
<button type="submit" class="btn btn-primary">Create Playlist</button>
162+
</form>
163+
</div>
164+
<div class="modal-footer">
165+
<button type="button" class="btn btn-dark w-100 text-white" data-bs-dismiss="modal">Close</button>
166+
</div>
167+
</div>
168+
</div>
169+
</div>
170+
122171
<style>
172+
.tile-bg {
173+
background-color: #2c2c2c;
174+
}
123175
img {
124176
height: 10rem;
125177
object-fit: contain;
126178
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.55);
127179
border: 1px solid rgba(0, 0, 0, 0.35);
128180
}
129181
130-
p{
182+
p {
131183
font-size: 1rem !important;
132184
font-weight: bolder;
133185
color: white;
134186
text-align: center;
135187
}
136188
137-
i{
189+
i {
138190
font-size: 1.2rem;
139-
color: #ACACAC;
191+
color: #acacac;
140192
font-weight: bolder;
141193
}
142194
143-
.modal-footer{
195+
.modal-footer {
144196
border: none !important;
145197
}
146198
147199
.modal-footer button {
148200
background-color: #2c2c2c !important;
149201
}
150202
203+
input[type="range"]::-webkit-slider-thumb {
204+
background-color: #1db954;
205+
}
151206
152-
input[type="range"]::-webkit-slider-thumb {
153-
background-color: #1DB954;
154-
}
155-
156-
input[type="range"]::-webkit-slider-runnable-track {
157-
background-color: #ACACAC;
158-
}
207+
input[type="range"]::-webkit-slider-runnable-track {
208+
background-color: #acacac;
209+
}
159210
160-
.modal-content {
211+
.modal-content {
161212
background-color: #1e1e1e !important;
162213
color: white;
163214
}
@@ -167,6 +218,6 @@ input[type="range"]::-webkit-slider-runnable-track {
167218
}
168219
169220
.form-range {
170-
color: #1DB954;
221+
color: #1db954;
171222
}
172223
</style>

0 commit comments

Comments
 (0)