Compare commits

...

9 Commits

Author SHA1 Message Date
44497c4429 Merge pull request 'save-and-restore-progress-for-audio fix #10' (#16) from save-and-restore-progress-for-audio into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #16
2023-09-19 16:34:56 +02:00
Artem Anufrij
25b9b7b582 disable play button on artist dialog
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2023-09-19 16:32:06 +02:00
Artem Anufrij
733b8eee91 reset progress for artists if playing is done
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-19 16:20:21 +02:00
Artem Anufrij
724f35dfb1 pregress handling for artists
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-19 15:16:23 +02:00
Artem Anufrij
62091a02a3 reset progress if album is finished
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-19 15:00:48 +02:00
Artem Anufrij
159935d949 added a play button and work on behavior
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-19 12:03:00 +02:00
Artem Anufrij
239b538c4b added first functions
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-19 01:56:09 +02:00
Artem Anufrij
8248073a4d prepare the intervals
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-18 17:41:42 +02:00
Artem Anufrij
ec9c9858f6 create and handle interval
All checks were successful
continuous-integration/drone/push Build is passing
2023-09-18 16:54:28 +02:00
6 changed files with 194 additions and 192 deletions

View File

@ -1,5 +1,5 @@
{
"backend_de": "http://localhost:31204",
"backend_dev": "https://webplay.rocks",
"backend_dev": "http://localhost:31204",
"backend_de": "https://webplay.rocks",
"backend": "https://webplay.rocks"
}

View File

@ -95,6 +95,14 @@ td.fillCell>* {
color: var(--yellow);
}
.keepPlaying {
z-index: 1;
position: absolute;
cursor: pointer;
right: 0;
}
/*
DIALOGS
*/

View File

@ -1,26 +1,9 @@
<template>
<div
id="player"
class="flex-column"
v-show="selectedTrack._id || selectedRadio._id"
>
<input
type="range"
id="slider"
min="0"
max="100"
step="0.1"
v-model="selectedTrack.percent"
@change="slideChanged"
/>
<div id="player" class="flex-column" v-show="selectedTrack._id || selectedRadio._id">
<input type="range" id="slider" min="0" max="100" step="0.1" v-model="selectedTrack.percent" @change="slideChanged" />
<div id="playerBar" class="flex-row">
<div class="flex-row grow">
<img
class="cover pointer"
:src="cover"
:title="selectedTrack.parent.title"
@click="gotoContainer"
/>
<img class="cover pointer" :src="cover" :title="selectedTrack.parent.title" @click="gotoContainer" />
<div v-if="selectedTrack._id" class="flex-column">
<b>{{ selectedTrack.title }}</b>
from
@ -28,21 +11,9 @@
</div>
</div>
<div id="playerControls" class="flex-row center">
<button
@click="switchShuffle"
title="Shuffle mode"
v-if="selectedTrack._id"
>
<img
src="static/icons/media-shuffle-dark.svg"
v-show="$store.getters['player/shuffle']"
class="small"
/>
<img
src="static/icons/media-consecutive-dark.svg"
v-show="$store.getters['player/shuffle'] == false"
class="small"
/>
<button @click="switchShuffle" title="Shuffle mode" v-if="selectedTrack._id">
<img src="static/icons/media-shuffle-dark.svg" v-show="$store.getters['player/shuffle']" class="small" />
<img src="static/icons/media-consecutive-dark.svg" v-show="$store.getters['player/shuffle'] == false" class="small" />
</button>
<button @click="prevTrack" title="Back" v-if="selectedTrack._id">
<awesome-icon icon="backward" />
@ -54,46 +25,17 @@
<button @click="nextTrack" title="Forward" v-if="selectedTrack._id">
<awesome-icon icon="forward" />
</button>
<button
@click="switchRepeatType"
title="Repeat mode"
v-if="selectedTrack._id"
>
<img
src="static/icons/media-repeat-dark.svg"
class="small"
v-show="$store.getters['player/repeatType'] == 'all'"
/>
<img
src="static/icons/media-repeat-song-dark.svg"
class="small"
v-show="$store.getters['player/repeatType'] == 'one'"
/>
<img
src="static/icons/media-no-repeat-dark.svg"
class="small"
v-show="$store.getters['player/repeatType'] == 'none'"
/>
<button @click="switchRepeatType" title="Repeat mode" v-if="selectedTrack._id">
<img src="static/icons/media-repeat-dark.svg" class="small" v-show="$store.getters['player/repeatType'] == 'all'" />
<img src="static/icons/media-repeat-song-dark.svg" class="small" v-show="$store.getters['player/repeatType'] == 'one'" />
<img src="static/icons/media-no-repeat-dark.svg" class="small" v-show="$store.getters['player/repeatType'] == 'none'" />
</button>
</div>
<div
class="flex-row ma-right hideOnMobilePortrait grow right"
v-show="selectedTrack.title"
>
<div class="flex-row ma-right hideOnMobilePortrait grow right" v-show="selectedTrack.title">
{{ formatedP }}&nbsp;|&nbsp;{{ formatedD }}
</div>
</div>
<audio
preload="auto"
ref="audioControl"
type="audio/mpeg"
@ended="nextTrack"
@canplay="play"
@playing="playing"
@durationchange="durationChanged"
@timeupdate="timeUpdate"
src
></audio>
<audio preload="auto" ref="audioControl" type="audio/mpeg" @ended="nextTrack" @canplay="play" @playing="playing" @durationchange="durationChanged" @timeupdate="timeUpdate" src></audio>
</div>
</template>
@ -105,7 +47,8 @@ export default {
audio: {},
duration: 0,
progress: 0,
interval: 0,
intervalProgress: 0,
intervalState: 0,
preConvert: false,
};
},
@ -121,21 +64,31 @@ export default {
play() {
this.audio.play();
},
pause() {
if (!this.audio.paused) {
this.audio.pause();
}
this.audio.pause();
window.clearInterval(this.intervalProgress);
window.clearInterval(this.intervalState);
this.pushState();
},
durationChanged() {
this.duration = this.audio.duration;
},
playing() {
window.clearInterval(this.interval);
this.interval = setInterval(() => {
window.clearInterval(this.intervalProgress);
window.clearInterval(this.intervalState);
this.intervalProgress = setInterval(() => {
this.progress = this.audio.currentTime;
this.selectedTrack.percent = (100 / this.duration) * this.progress;
}, 500);
if (this.currentUser._id) {
this.intervalState = setInterval(() => {
this.pushState();
}, 10000);
}
},
audioReset() {
this.audio.pause();
@ -156,6 +109,14 @@ export default {
this.audio.play();
}
},
skipToSecond(second) {
let was_paused = this.audio.paused;
this.audio.pause();
this.audio.currentTime = second;
if (!was_paused) {
this.audio.play();
}
},
playRadio(radio) {
this.$store.commit("tracks/resetSelectedTrack");
this.audio.pause();
@ -184,8 +145,13 @@ export default {
this.pushHistoryItem();
// Try to fix SAFARI
this.audio.play();
if (this.currentTrackParent.progress) {
this.skipToSecond(this.currentTrackParent.progress.progress);
this.currentTrackParent.progress = undefined;
} else {
// Try to fix SAFARI
this.audio.play();
}
},
pushHistoryItem() {
if (!this.currentUser._id) {
@ -232,13 +198,20 @@ export default {
return;
}
if (!this.audio.paused) {
this.audio.pause();
this.pause();
} else if (this.audio.src != "") {
this.audio.play();
}
},
reset() {
window.clearInterval(this.interval);
reset(item) {
let parentId = item.parent._id;
if (item.parent.parent && item.parent.parent.tracks) {
parentId = item.parent.parent._id;
}
this.$store.dispatch("user/resetProgress", parentId);
window.clearInterval(this.intervalProgress);
window.clearInterval(this.intervalState);
if (!this.audio.paused) {
this.audio.pause();
}
@ -299,6 +272,19 @@ export default {
}
this.$store.dispatch("user/savePlayerSettings");
},
pushState() {
if (!this.currentUser._id) {
return;
}
this.progress = this.audio.currentTime;
let item = {
id: this.selectedTrack._id,
parentId: this.currentTrackParent._id,
type: "track",
progress: Math.round(this.progress)
}
this.$store.dispatch("user/saveProgress", item);
},
timeUpdate(event) {
let percent = (event.target.currentTime / event.target.duration) * 100;
if (percent > 10 && !this.preConvert) {
@ -390,11 +376,11 @@ export default {
this.reset();
}
},
selectedTrack(newVal) {
selectedTrack(newVal, oldVal) {
if (newVal._id) {
this.playTrack(newVal);
} else {
this.reset();
this.reset(oldVal);
}
},
},
@ -409,17 +395,21 @@ export default {
z-index: 1001;
box-shadow: 0 0px 4px var(--shadow);
}
#player .cover {
width: 52px;
margin-right: 4px;
}
#playerBar {
overflow: hidden;
max-height: 52px;
}
#playerBar > div {
#playerBar>div {
align-items: center;
}
#playerControls button {
display: flex;
padding: 4px 12px;

View File

@ -1,25 +1,13 @@
<template>
<DialogBase
ref="dialogWindow"
:title="album_title"
@canceled="closed"
:showFooter="false"
:disableXscroll="true"
:disableYscroll="true"
>
<DialogBase ref="dialogWindow" :title="album_title" @canceled="closed" :showFooter="false" :disableXscroll="true" :disableYscroll="true">
<div id="albumViewer" class="flex-row">
<div id="header" class="flex-column">
<div id="background" :style="coverBackground" />
<div class="grow z1 center flex-column">
<img class="glow ma24" :src="cover" @dblclick="dblclick" />
</div>
<awesome-icon
icon="star"
size="2x"
class="favourite ma4"
:class="{ active: isFavourite }"
@click="toggleFavourite"
/>
<awesome-icon icon="star" size="2x" class="favourite ma4" :class="{ active: isFavourite }" @click="toggleFavourite" title="Favourite" />
<awesome-icon icon="play" size="2x" class="keepPlaying ma4 primary-text" @click="playProgress" v-if="selectedAlbum.progress" title="Keep playing" />
<div id="stats" class="flex-row z1">
<DropDown v-if="$store.getters['user/isAdministrator']">
<button class="flat center" :title="visibility_text">
@ -27,11 +15,7 @@
</button>
<template v-slot:dropdown-content>
<div>
<button
v-for="(item, i) in $store.state.system.lists.visibility"
:key="i"
@click="setVisibility(item)"
>
<button v-for="(item, i) in $store.state.system.lists.visibility" :key="i" @click="setVisibility(item)">
<awesome-icon :icon="getVisibilityIcon(item)" />{{
getVisibilityText(item)
}}
@ -40,10 +24,7 @@
<button v-if="!selectedAlbum.share._id" @click="shareEnable">
<awesome-icon icon="share" />Share this album
</button>
<button
v-if="selectedAlbum.share._id"
@click="addShareUrlToClipboard"
>
<button v-if="selectedAlbum.share._id" @click="addShareUrlToClipboard">
<awesome-icon icon="clipboard" />Copy url into clipboard
</button>
<button v-if="selectedAlbum.share._id" @click="shareDisable">
@ -61,8 +42,7 @@
}}</b>
<br />
<span v-if="album_year">
from year <b>{{ album_year }}</b> </span
><br />
from year <b>{{ album_year }}</b> </span><br />
<b>{{ album_tracks.length }}</b> Tracks with a duration of
<b>{{ album_duration }}</b>
</span>
@ -89,7 +69,6 @@
</DropDown>
</div>
</div>
<ul id="trackList" class="tracks">
<li v-for="track in selectedAlbum.tracks" :key="track._id">
<TrackItem :track="track" :showCover="false" />
@ -156,6 +135,14 @@ export default {
}
}
},
playProgress() {
let track = this.selectedAlbum.tracks.find(
(f) => f._id == this.selectedAlbum.progress.id
);
if (track) {
this.$store.dispatch("tracks/play", track);
}
},
closed() {
if (
(window.history.state.back &&
@ -201,19 +188,19 @@ export default {
return visibility == "global"
? "globe"
: visibility == "instance"
? "server"
: visibility == "hidden"
? "eye-slash"
: "user";
? "server"
: visibility == "hidden"
? "eye-slash"
: "user";
},
getVisibilityText(visibility) {
return visibility == "global"
? "Global"
: visibility == "instance"
? "On this server"
: visibility == "hidden"
? "Hide this Album"
: "Only for me";
? "On this server"
: visibility == "hidden"
? "Hide this Album"
: "Only for me";
},
selectAlbum(album) {
this.$store.dispatch("albums/selectAlbum", album);
@ -228,6 +215,10 @@ export default {
shareDisable() {
this.$store.dispatch("albums/shareDisable", this.selectedAlbum);
},
loadUserProgress() {
if (this.selectedTrack.parent._id != this.selectedAlbum._id)
this.$store.dispatch("user/getProgress", this.selectedAlbum);
}
},
computed: {
...mapGetters({
@ -287,19 +278,19 @@ export default {
return this.selectedAlbum.visibility == "global"
? "globe"
: this.selectedAlbum.visibility == "instance"
? "server"
: this.selectedAlbum.visibility == "hidden"
? "eye-slash"
: "user";
? "server"
: this.selectedAlbum.visibility == "hidden"
? "eye-slash"
: "user";
},
visibility_text() {
return this.selectedAlbum.visibility == "global"
? "Visible for the whole world"
: this.selectedAlbum.visibility == "instance"
? "Visible on this instance"
: this.selectedAlbum.visibility == "hidden"
? "Hidden for all users"
: "Visible only for me";
? "Visible on this instance"
: this.selectedAlbum.visibility == "hidden"
? "Hidden for all users"
: "Visible only for me";
},
isFavourite() {
return (
@ -318,6 +309,7 @@ export default {
this.$refs.dialogWindow.open();
window.addEventListener("keydown", this.keydownListener);
}
this.loadUserProgress();
this.gotoTrack();
} else {
if (this.$refs.dialogWindow.visible) {
@ -339,14 +331,17 @@ export default {
height: 366px;
width: 640px;
}
#header {
position: relative;
background-color: black;
}
#header img {
align-self: center;
width: 256px;
}
#stats {
z-index: 2;
align-items: center;
@ -357,10 +352,12 @@ export default {
border-top: 1px solid #ffffff20;
border-bottom: 1px solid #00000020;
}
.dropdown-activator button {
width: 32px;
height: 32px;
}
#trackList {
background-color: var(--white);
z-index: 1;
@ -370,15 +367,19 @@ export default {
#header {
width: 100%;
}
#albumViewer {
flex-direction: column;
}
}
@media (max-width: 480px), (max-height: 480px) {
@media (max-width: 480px),
(max-height: 480px) {
#albumViewer {
width: 100%;
height: 100%;
}
#trackList {
height: initial;
flex-grow: 1;

View File

@ -1,24 +1,10 @@
<template>
<DialogBase
ref="dialogWindow"
id="dialogWindow"
:title="selectedArtist.name"
@canceled="closed"
:showFooter="false"
:showFullscreenButton="true"
:disableXscroll="true"
:disableYscroll="true"
>
<DialogBase ref="dialogWindow" id="dialogWindow" :title="selectedArtist.name" @canceled="closed" :showFooter="false" :showFullscreenButton="true" :disableXscroll="true" :disableYscroll="true">
<div id="artistViewer">
<div id="header" class="flex-column">
<div id="background" :style="coverBackground" />
<awesome-icon
icon="star"
size="2x"
class="favourite ma4"
:class="{ active: isFavourite }"
@click="toggleFavourite"
/>
<awesome-icon icon="star" size="2x" class="favourite ma4" :class="{ active: isFavourite }" @click="toggleFavourite" />
<awesome-icon icon="play" size="2x" class="keepPlaying ma4 primary-text" @click="playProgress" v-if="selectedArtist.progress" title="Keep playing" />
<h1 @dblclick="dblclick">
{{ selectedArtist.name }}
</h1>
@ -28,41 +14,20 @@
<b>{{ artist_duration }}</b>
</span>
<div id="albumList" class="flex-row showOnMobilePortrait">
<AlbumItem
class="ma"
:class="{ playing: playingAlbumId == album._id }"
v-for="album in selectedArtist.albums"
:key="album._id"
:item="album"
@click="scrollToAlbum(album)"
@dblclick="playAlbum(album)"
/>
<AlbumItem class="ma" :class="{ playing: playingAlbumId == album._id }" v-for="album in selectedArtist.albums" :key="album._id" :item="album" @click="scrollToAlbum(album)" @dblclick="playAlbum(album)" />
</div>
<div id="navigation" class="flex-row center ma-top">
<div class="flex-row grow"></div>
<div class="flex-row">
<button
@click="gotoPrevArtist"
class="primary ma4"
:title="prevArtist.name"
:disabled="!prevArtist._id"
>
<button @click="gotoPrevArtist" class="primary ma4" :title="prevArtist.name" :disabled="!prevArtist._id">
<awesome-icon icon="angle-left" class="ma4" />
</button>
<button
@click="gotoNextArtist"
class="primary ma4"
:title="nextArtist.name"
:disabled="!nextArtist._id"
>
<button @click="gotoNextArtist" class="primary ma4" :title="nextArtist.name" :disabled="!nextArtist._id">
<awesome-icon icon="angle-right" class="ma4" />
</button>
</div>
<div class="flex-row grow right center">
<DropDown
v-if="$store.getters['user/isAdministrator']"
class="hideOnMobile"
>
<DropDown v-if="$store.getters['user/isAdministrator']" class="hideOnMobile">
<button class="flat pa8-left pa8-right">
<awesome-icon icon="ellipsis-v" />
</button>
@ -86,23 +51,9 @@
</div>
<div class="flex-row overflow-y">
<div id="albumList" class="flex-column hideOnMobilePortrait">
<AlbumItem
class="ma-top ma-left ma-right"
:class="{ playing: playingAlbumId == album._id }"
v-for="album in selectedArtist.albums"
:key="album._id"
:item="album"
:id="album._id"
:ref="album._id"
@click="scrollToAlbum(album)"
@dblclick="playAlbum(album)"
/>
<AlbumItem class="ma-top ma-left ma-right" :class="{ playing: playingAlbumId == album._id }" v-for="album in selectedArtist.albums" :key="album._id" :item="album" :id="album._id" :ref="album._id" @click="scrollToAlbum(album)" @dblclick="playAlbum(album)" />
</div>
<ul
id="trackList"
class="tracks"
:class="{ playing: selectedTrack._id != null }"
>
<ul id="trackList" class="tracks" :class="{ playing: selectedTrack._id != null }">
<li v-for="track in selectedArtist.tracks" :key="track._id">
<TrackItem :track="track" :ref="track._id" />
</li>
@ -143,6 +94,14 @@ export default {
}
}
},
playProgress() {
let track = this.selectedArtist.tracks.find(
(f) => f._id == this.selectedArtist.progress.id
);
if (track) {
this.$store.dispatch("tracks/play", track);
}
},
gotoNextArtist() {
this.$store.dispatch("artists/gotoNextArtist");
},
@ -194,6 +153,10 @@ export default {
uploadNewCover() {
this.$store.dispatch("artists/uploadNewCover", this.selectedArtist);
},
loadUserProgress() {
if (!this.isPlaying || this.selectedTrack.parent.parent._id != this.selectedArtist._id)
this.$store.dispatch("user/getProgress", this.selectedArtist);
}
},
computed: {
...mapGetters({
@ -202,6 +165,7 @@ export default {
selectedArtist: ["artists/selectedArtist"],
selectedTrack: ["tracks/selectedTrack"],
favourites: ["user/favourites"],
isPlaying: ["player/isPlaying"]
}),
cover() {
let covers = this.selectedArtist.covers;
@ -266,6 +230,7 @@ export default {
this.$refs.dialogWindow.open();
window.addEventListener("keydown", this.keydownListener);
}
this.loadUserProgress();
this.gotoTrack();
} else {
if (this.$refs.dialogWindow.visible) {
@ -289,6 +254,7 @@ export default {
flex-direction: column;
overflow: hidden;
}
h1,
#stats {
z-index: 1;
@ -297,33 +263,40 @@ h1,
color: var(--white);
text-shadow: 0 1px 2px black;
}
#artistImage {
width: 512px;
max-height: 256px;
}
#header {
position: relative;
background-color: black;
width: 760px;
max-width: 100%;
}
#albumList {
z-index: 1;
overflow-y: auto;
background-color: var(--white);
}
#albumList::-webkit-scrollbar {
display: none;
}
#albumList .album:last-child {
margin-bottom: 12px;
}
#navigation {
z-index: 2;
background-color: #ffffff40;
border-top: 1px solid #ffffff20;
border-bottom: 1px solid #00000020;
}
#trackList {
z-index: 1;
background-color: var(--white);
@ -338,12 +311,15 @@ h1,
width: initial;
height: initial;
}
.dialog-body button {
color: var(--darkgray);
}
.container {
flex-grow: 0;
}
@media (max-width: 480px) {
#albumList {
background-color: initial;
@ -353,19 +329,21 @@ h1,
}
}
@media (max-width: 480px), (max-height: 480px) {
@media (max-width: 480px),
(max-height: 480px) {
#artistViewer {
height: initial;
}
#trackList {
width: initial;
height: initial;
}
#header {
width: initial;
}
}
@media (max-height: 480px) {
}
@media (max-height: 480px) {}
</style>

View File

@ -80,6 +80,31 @@ export default {
context.commit("setHistory", res.data);
});
},
saveProgress(context, item) {
if (context.state._id == -1) {
return;
}
axios
.post(context.rootGetters.server + "/api/user/progress", item, context.rootGetters.headers);
},
getProgress(context, parent) {
if (context.state._id == -1) {
return;
}
axios
.get(context.rootGetters.server + "/api/user/progress/" + parent._id, context.rootGetters.headers)
.then((res) => {
parent.progress = res.data;
});
},
resetProgress(context, parentId) {
if (context.state._id == -1) {
return;
}
axios
.delete(context.rootGetters.server + "/api/user/progress/" + parentId, context.rootGetters.headers);
},
savePlayerSettings(context) {
let body = {
repeat: context.rootGetters["player/repeatType"],