Skip to content

Commit 44b5768

Browse files
authored
Feature/sync playlist button send a download request for an existing playlist (#90)
* update playlist endpoint, added sync call * Thats all folks * Thats all folks .. again * A clown walking back because he forgot his shoes
1 parent 8ede8d9 commit 44b5768

9 files changed

Lines changed: 127 additions & 11 deletions

File tree

MyMusicBoxApi/database/playlisttable.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type IPlaylistTable interface {
1111
FetchPlaylists(ctx context.Context, lastKnowPlaylistId int) (playlists []models.Playlist, error error)
1212
InsertPlaylist(playlist models.Playlist) (lastInsertedId int, error error)
1313
DeletePlaylist(playlistId int) (error error)
14+
FetchPlaylistsById(ctx context.Context, playlistId int) (plst models.Playlist, error error)
1415
}
1516

1617
type PlaylistTable struct {
@@ -54,6 +55,24 @@ func (table *PlaylistTable) FetchPlaylists(ctx context.Context, lastKnowPlaylist
5455
return playlists, nil
5556
}
5657

58+
func (table *PlaylistTable) FetchPlaylistsById(ctx context.Context, playlistId int) (plst models.Playlist, error error) {
59+
query := "SELECT Id, Name, ThumbnailPath, Description, CreationDate, IsPublic FROM Playlist WHERE Id = $1" //
60+
//rows, err := table.QueryRowsContex(ctx, query, playlistId)
61+
62+
row := table.QueryRow(query, playlistId)
63+
64+
var playlist models.Playlist
65+
66+
scanError := row.Scan(&playlist.Id, &playlist.Name, &playlist.ThumbnailPath, &playlist.Description, &playlist.CreationDate, &playlist.IsPublic)
67+
68+
if scanError != nil {
69+
logging.Error(fmt.Sprintf("Scan error: %s", scanError.Error()))
70+
return models.Playlist{}, scanError
71+
}
72+
73+
return playlist, nil
74+
}
75+
5776
func (table *PlaylistTable) InsertPlaylist(playlist models.Playlist) (lastInsertedId int, error error) {
5877
query := `INSERT INTO Playlist (name, description, thumbnailPath, ispublic) VALUES ($1, $2, $3, $4) RETURNING Id`
5978

MyMusicBoxApi/go.mod

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ require (
3333
github.com/josharian/intern v1.0.0 // indirect
3434
github.com/mailru/easyjson v0.9.0 // indirect
3535
github.com/pmezard/go-difflib v1.0.0 // indirect
36-
github.com/ulikunitz/xz v0.5.13 // indirect
37-
golang.org/x/mod v0.27.0 // indirect
38-
golang.org/x/sync v0.16.0 // indirect
39-
golang.org/x/tools v0.36.0 // indirect
36+
github.com/ulikunitz/xz v0.5.15 // indirect
37+
golang.org/x/mod v0.29.0 // indirect
38+
golang.org/x/sync v0.18.0 // indirect
39+
golang.org/x/tools v0.38.0 // indirect
4040
)
4141

4242
require (
@@ -54,18 +54,18 @@ require (
5454
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
5555
github.com/leodido/go-urn v1.4.0 // indirect
5656
github.com/lib/pq v1.10.9
57-
github.com/lrstanley/go-ytdlp v1.2.6
57+
github.com/lrstanley/go-ytdlp v1.2.7
5858
github.com/mattn/go-isatty v0.0.20 // indirect
5959
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
6060
github.com/modern-go/reflect2 v1.0.2 // indirect
6161
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
6262
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
6363
github.com/ugorji/go/codec v1.3.0 // indirect
6464
golang.org/x/arch v0.20.0 // indirect
65-
golang.org/x/crypto v0.41.0 // indirect
66-
golang.org/x/net v0.43.0 // indirect
67-
golang.org/x/sys v0.35.0 // indirect
68-
golang.org/x/text v0.28.0 // indirect
65+
golang.org/x/crypto v0.44.0 // indirect
66+
golang.org/x/net v0.46.0 // indirect
67+
golang.org/x/sys v0.38.0 // indirect
68+
golang.org/x/text v0.31.0 // indirect
6969
google.golang.org/protobuf v1.36.8 // indirect
7070
gopkg.in/yaml.v3 v3.0.1 // indirect
7171
)

MyMusicBoxApi/go.sum

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
8585
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
8686
github.com/lrstanley/go-ytdlp v1.2.6 h1:LJ1I+uaP2KviRAfe3tUN0Sd4yI9XlCJBG37RCH+sfq8=
8787
github.com/lrstanley/go-ytdlp v1.2.6/go.mod h1:38IL64XM6gULrWtKTiR0+TTNCVbxesNSbTyaFG2CGTI=
88+
github.com/lrstanley/go-ytdlp v1.2.7 h1:YNDvKkd0OCJSZLZePZvJwcirBCfL8Yw3eCwrTCE5w7Q=
89+
github.com/lrstanley/go-ytdlp v1.2.7/go.mod h1:38IL64XM6gULrWtKTiR0+TTNCVbxesNSbTyaFG2CGTI=
8890
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
8991
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
9092
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -117,23 +119,39 @@ github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA
117119
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
118120
github.com/ulikunitz/xz v0.5.13 h1:ar98gWrjf4H1ev05fYP/o29PDZw9DrI3niHtnEqyuXA=
119121
github.com/ulikunitz/xz v0.5.13/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
122+
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
123+
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
120124
golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c=
121125
golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
122126
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
123127
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
128+
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
129+
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
124130
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
125131
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
132+
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
133+
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
126134
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
127135
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
136+
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
137+
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
128138
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
129139
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
140+
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
141+
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
130142
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
131143
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
132144
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
145+
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
146+
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
133147
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
134148
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
149+
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
150+
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
135151
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
136152
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
153+
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
154+
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
137155
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
138156
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
139157
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

MyMusicBoxApi/http/playlist.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"musicboxapi/database"
77
"musicboxapi/logging"
88
"musicboxapi/models"
9+
"musicboxapi/service"
910
"net/http"
1011
"path/filepath"
1112
"strconv"
@@ -43,6 +44,42 @@ func (handler *PlaylistHandler) FetchPlaylists(ctx *gin.Context) {
4344
ctx.JSON(http.StatusOK, models.OkResponse(playlists, fmt.Sprintf("Found %d playlist", len(playlists))))
4445
}
4546

47+
func (handler *PlaylistHandler) SyncPlaylist(ctx *gin.Context) {
48+
49+
playlistIdParameter := ctx.Param("playlistId")
50+
51+
id, err := strconv.Atoi(playlistIdParameter)
52+
53+
if err != nil {
54+
ctx.JSON(http.StatusInternalServerError, models.ErrorResponse(err))
55+
return
56+
}
57+
58+
if DefaultPlaylistId == id {
59+
ctx.JSON(http.StatusInternalServerError, models.ErrorResponse("Funky music... wrong year"))
60+
return
61+
}
62+
63+
playlistTable := database.NewPlaylistTableInstance()
64+
65+
playlist, err := playlistTable.FetchPlaylistsById(ctx.Request.Context(), id)
66+
67+
if err != nil {
68+
ctx.JSON(http.StatusInternalServerError, models.ErrorResponse(err))
69+
return
70+
}
71+
72+
playlistId := strings.Split(playlist.ThumbnailPath, ".")[0]
73+
74+
var request models.DownloadRequestModel
75+
76+
request.Url = fmt.Sprintf("https://www.youtube.com/playlist?list=%s", playlistId)
77+
78+
go service.StartDownloadTask(request)
79+
80+
ctx.JSON(http.StatusOK, models.OkResponse(gin.H{"": ""}, "Created"))
81+
}
82+
4683
func (hanlder *PlaylistHandler) InsertPlaylist(ctx *gin.Context) {
4784

4885
var playlistModel models.CreatePlaylistModel

MyMusicBoxApi/http/v1.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func V1Endpoints(apiv1Group *gin.RouterGroup) {
3737
apiv1Group.GET("/tasklogs/:parentId", taskLogHandler.FetchChildTaskLogs)
3838

3939
apiv1Group.POST("/playlist", playlistHandler.InsertPlaylist)
40+
apiv1Group.POST("/playlist/sync/:playlistId", playlistHandler.SyncPlaylist)
4041
apiv1Group.POST("/playlistsong/:playlistId/:songId", playlistSongHandler.InsertPlaylistSong)
4142
apiv1Group.POST("/download", DownloadRequest)
4243

MyMusicBoxApi/service/ytdlp.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ func StartDownloadTask(downloadRequest models.DownloadRequestModel) {
7474
PrintToFile("%(duration)s", durationFileName).
7575
PrintToFile("%(playlist_title)s", playlistTitleFileName).
7676
PrintToFile("%(playlist_id)s", playlistIdFileName).
77-
Cookies("selenium/cookies_netscape")
77+
Cookies("selenium/cookies_netscape").
78+
IgnoreErrors()
7879

7980
// Start download (flat download)
8081
result, err := dlp.Run(context.Background(), downloadRequest.Url)

MyMusicClientSveltePwa/src/App.svelte

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
import { initializeMediaSessionService } from "./lib/scripts/mediasessionService.js";
1313
import { searchQuery } from "./lib/scripts/util.js";
1414
import SearchBar from "./lib/components/SearchBar.svelte";
15+
import { syncPlaylistById } from "./lib/scripts/api.js";
1516
1617
$: $pathName;
18+
$: $componentParams;
1719
$: $currentView;
1820
1921
// @ts-ignore
@@ -52,6 +54,16 @@
5254
async function refresh() {
5355
window.location.reload();
5456
}
57+
58+
async function syncPlaylist() {
59+
const playlistId = get(componentParams).playlistId
60+
if (playlistId) {
61+
// alert(`Syncing playlist ${playlistId} not yet implemented.`);
62+
await syncPlaylistById(playlistId);
63+
} else {
64+
alert("No playlist selected to sync.");
65+
}
66+
}
5567
</script>
5668

5769
<div class="app-layout">
@@ -92,7 +104,8 @@
92104
<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>
93105

94106
<div id="actions" class="dropdown-menu w-100">
95-
<button class="btn btn-primary dropdown-item" data-bs-toggle="modal" data-bs-target="#createPlaylistModal" >New Playlist</button>
107+
<button class="btn btn-primary text-success dropdown-item" on:click={syncPlaylist} disabled={!$pathName.includes("playlists")}>Sync Playlist</button>
108+
<button class="btn btn-primary text-info dropdown-item" data-bs-toggle="modal" data-bs-target="#createPlaylistModal" >New Playlist</button>
96109
<button class="btn btn-primary dropdown-item text-danger" on:click={deleteCurrentPlaylist} disabled={!$pathName.includes("playlists")}>Delete Current Playlist</button>
97110
</div>
98111
</div>

MyMusicClientSveltePwa/src/lib/components/Playlist.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<p>{songCount} songs • {playlist.description}</p>
3636
</div>
3737
</article>
38+
3839
</div>
3940
{:else}
4041
<p>No playlist founnd with id: {playlist.id}.</p>

MyMusicClientSveltePwa/src/lib/scripts/api.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,32 @@ export async function deleteSongFromPlaylist(playlistId, songId) {
135135
}
136136
}
137137

138+
export async function syncPlaylistById(playlistId) {
139+
try {
140+
const response = await fetch(`${baseApiUrl}/playlist/sync/${playlistId}`, {
141+
method: "POST",
142+
});
143+
144+
var cd = response.json()
145+
146+
console.log("Sync playlist response:", cd)
147+
148+
if (!response.ok) {
149+
throw new Error(`HTTP error! status: ${response}`);
150+
}
151+
return {
152+
success: true,
153+
data: response,
154+
};
155+
} catch (error) {
156+
console.log("Error syncing playlist:", error);
157+
return {
158+
success: false,
159+
data: error,
160+
};
161+
}
162+
}
163+
138164
export function getImageUrl(path) {
139165
var config = getConfiguration();
140166

0 commit comments

Comments
 (0)