diff --git a/src/modules/album/album.go b/src/modules/album/album.go
index fade5d1..0cd2daf 100644
--- a/src/modules/album/album.go
+++ b/src/modules/album/album.go
@@ -3,6 +3,7 @@ package album
import (
"acide/src/modules/song"
"acide/src/utils"
+ "bytes"
"encoding/json"
"net/http"
"sync"
@@ -89,9 +90,9 @@ func albumPage(c echo.Context) error {
}
// convert the song list to json
- clientSons := make([]ClientSong, len(songs))
+ clientSongs := make([]ClientSong, len(songs))
for i, song := range songs {
- clientSons[i] = ClientSong{
+ clientSongs[i] = ClientSong{
Title: song.Title,
Artist: song.Artist,
AlbumId: album.ID,
@@ -99,11 +100,15 @@ func albumPage(c echo.Context) error {
SongId: song.ID,
}
}
- clientSongsJson, err := json.Marshal(clientSons)
- if err != nil {
+
+ var buff bytes.Buffer
+ enc := json.NewEncoder(&buff)
+ enc.SetEscapeHTML(false)
+ if err := enc.Encode(&clientSongs); err != nil {
log.Printf("Error marshaling clientSongs: %s", err)
return err
}
+ clientSongsJson := buff.String()
return utils.RenderTempl(c, http.StatusOK, albumTempl(albumId, album, songs, string(clientSongsJson)))
}
diff --git a/src/utils/utils.templ b/src/utils/utils.templ
index eadfa55..6fd9cf6 100644
--- a/src/utils/utils.templ
+++ b/src/utils/utils.templ
@@ -88,33 +88,73 @@ templ MusicPlayer() {
volume: 0.1,
playing: false,
loading: false,
+ progress: 0,
+ // sets the queue, and plays the song at idx
replaceQueueAndPlayAt(queue, idx) {
this.queue = queue;
this.idx = idx;
- this.play();
- },
- // Plays the song at the current position
- async play() {
- const songId = this.queue[this.idx].songId;
+ this.play(idx);
+
+ // setup the preload and progress listener
+ setInterval(() => this.checkDuration(), 1000);
+ },
+
+ // Plays the song at the passed idx, sets this.idx to that,
+ // and plays a preloaded song
+ //
+ // If preloaded=true, this function will assume that it is the
+ // next song. It will trust that idx is correct.
+ async play(idx) {
+ const preloaded = this.nextSound !== null && idx === this.idx + 1;
+
+ // if a song is currently playing
+ // then fade it out before playing the next sound
+ if (this.playing === true
+ && this.currentSound !== null
+ ) {
+ // this will not trigger when next() is called,
+ // because next() sets this.playing=false
- if (this.currentSound !== null) {
this.currentSound.fade(this.volume, 0.0, 250);
await wait(250);
- this.currentSound.unload();
}
- const sound = new Howl({
- src: `https://navidrome.araozu.dev/rest/stream.view?id=${songId}&v=1.13.0&c=music-to-go&u=fernando&s=49805d&t=4148cd1c83ae1bd01334facf4e70a947`,
- html5: true,
- volume: this.volume,
- })
- this.loading = true;
- sound.play();
- let preloadInterval;
- sound.once("load", () => {
+ this.currentSound?.unload?.();
+ this.playing = false;
+ this.currentSound = null;
+ this.idx = idx;
+
+ // if a song is preloaded, assume it's the next song and play it
+ if (preloaded === true && this.nextSound !== null) {
+ this.currentSound = this.nextSound;
+ this.nextSound = null;
+ this.currentSound.play();
+ this.playing = true;
+ } else {
+ // otherwise, load the song at idx and play it
+ const songId = this.queue[idx].songId;
+
+ const sound = new Howl({
+ src: `https://navidrome.araozu.dev/rest/stream.view?id=${songId}&v=1.13.0&c=music-to-go&u=fernando&s=49805d&t=4148cd1c83ae1bd01334facf4e70a947`,
+ html5: true,
+ volume: this.volume,
+ });
+ this.currentSound = sound;
+ this.loading = true;
+ sound.play();
+ sound.once("load", () => {
+ this.loading = false;
+ this.playing = true;
+ });
+ }
+
+ // set-up preloading for the next song
+ const sound = this.currentSound;
+ sound.once("play", () => {
const length = sound.duration();
const targetLength = length - 5;
+ let preloadInterval;
preloadInterval = setInterval(() => {
const pos = sound.seek();
if (pos > targetLength) {
@@ -122,22 +162,37 @@ templ MusicPlayer() {
clearInterval(preloadInterval);
}
}, 1000);
- this.loading = false;
- this.playing = true;
});
+
+ // set-up playing the next song when the current finishes
sound.once("end", () => {
this.playing = false;
this.next();
});
this.currentSound = sound;
},
- async playNext() {
- this.currentSound?.unload();
- this.nextSound.play();
- this.playing = true;
- this.currentSound = this.nextSound;
- this.nextSound = null;
+
+ // checks the duration of the playing song and:
+ // - updates the song progress (0-100%)
+ // - begins preloading
+ checkDuration() {
+ const sound = this.currentSound;
+ if (this.playing) {
+ const length = sound.duration();
+ if (length <= 0) return;
+
+ const position = sound.seek();
+
+ // preload 5s before the song ends
+ if (position >= length - 5 && this.nextSound === null) {
+ this.preload();
+ }
+
+ // update the song progress percentage
+ this.progress = Math.floor((position * 100) / length);
+ }
},
+
togglePlayPause() {
if (this.playing === true) {
this.playing = false;
@@ -149,8 +204,7 @@ templ MusicPlayer() {
},
next() {
if (this.idx + 1 < this.queue.length) {
- this.idx += 1;
- this.playNext();
+ this.play(this.idx + 1);
}
},
preload() {
@@ -166,30 +220,13 @@ templ MusicPlayer() {
volume: 0,
preload: true,
});
- // Attempt to play immediately the song, immediately pause it, rewind it and set volume back up
+ // Attempt to immediately play the song, immediately pause it, rewind it and set volume back up
nextSound.play();
-
let preloadInterval;
nextSound.once("load", () => {
nextSound.pause();
nextSound.seek(0);
nextSound.volume(this.volume);
-
- const length = nextSound.duration();
- const targetLength = length - 5;
- preloadInterval = setInterval(() => {
- const pos = nextSound.seek();
- if (pos > targetLength) {
- this.preload();
- clearInterval(preloadInterval);
- }
- }, 1000);
- this.loading = false;
- this.playing = true;
- });
- nextSound.once("end", () => {
- this.playing = false;
- this.next();
});
this.nextSound = nextSound;
}
diff --git a/src/utils/utils_templ.go b/src/utils/utils_templ.go
index 688ecbb..a91ebdc 100644
--- a/src/utils/utils_templ.go
+++ b/src/utils/utils_templ.go
@@ -100,7 +100,7 @@ func MusicPlayer() templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
@@ -181,7 +181,7 @@ func playIcon(size int) templ.Component {
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 250, Col: 28}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 287, Col: 28}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
@@ -194,7 +194,7 @@ func playIcon(size int) templ.Component {
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 251, Col: 29}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 288, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
@@ -236,7 +236,7 @@ func pauseIcon(size int) templ.Component {
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 271, Col: 28}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 308, Col: 28}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
@@ -249,7 +249,7 @@ func pauseIcon(size int) templ.Component {
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 272, Col: 29}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 309, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
@@ -291,7 +291,7 @@ func skipForwardIcon(size int) templ.Component {
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 280, Col: 67}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 317, Col: 67}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
@@ -304,7 +304,7 @@ func skipForwardIcon(size int) templ.Component {
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 280, Col: 97}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 317, Col: 97}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
@@ -346,7 +346,7 @@ func circleNotchIcon(size int) templ.Component {
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 294, Col: 28}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 331, Col: 28}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
@@ -359,7 +359,7 @@ func circleNotchIcon(size int) templ.Component {
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 295, Col: 29}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 332, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
@@ -401,7 +401,7 @@ func playlistIcon(size int) templ.Component {
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 310, Col: 28}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 347, Col: 28}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
@@ -414,7 +414,7 @@ func playlistIcon(size int) templ.Component {
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 311, Col: 29}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 348, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
if templ_7745c5c3_Err != nil {
@@ -456,7 +456,7 @@ func caretDoubleDownIcon(size int) templ.Component {
var templ_7745c5c3_Var20 string
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 325, Col: 28}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 362, Col: 28}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
if templ_7745c5c3_Err != nil {
@@ -469,7 +469,7 @@ func caretDoubleDownIcon(size int) templ.Component {
var templ_7745c5c3_Var21 string
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(size))
if templ_7745c5c3_Err != nil {
- return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 326, Col: 29}
+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/utils/utils.templ`, Line: 363, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
if templ_7745c5c3_Err != nil {