client: add progress bar

- add favourites buttons
- redirect /folder/ => /folder/home
- minor fixes
This commit is contained in:
geoffrey45 2021-12-24 12:41:38 +03:00
parent 81c8ae8629
commit 757d6cbe5a
12 changed files with 338 additions and 154 deletions

View File

@ -23,11 +23,7 @@
<div class="m-np">
<NowPlaying />
</div>
<UpNext
v-model:up_next="up_next"
@expandQueue="expandQueue"
:queue="queue"
/>
<UpNext v-model:up_next="up_next" @expandQueue="expandQueue" />
<RecommendedArtist />
</div>
</div>
@ -58,7 +54,6 @@ export default {
setup() {
const collapsed = ref(true);
const queue = ref(JSON.parse(localStorage.getItem("queue")) || []);
perks.readQueue();
@ -91,7 +86,6 @@ export default {
expandSearch,
collapseSearch,
search,
queue,
};
},
};
@ -118,10 +112,6 @@ export default {
animation-iteration-count: 1;
}
.l-sidebar {
position: relative;
}
.l-container #toggle {
position: absolute;
left: 0.2rem;
@ -134,8 +124,9 @@ export default {
cursor: pointer;
}
// .m-np {
// position: absolute;
// bottom: 0;
// }
.r-sidebar {
&::-webkit-scrollbar {
display: none;
}
}
</style>

View File

@ -12,8 +12,7 @@ body {
margin: 0;
background: #0d0e0e;
color: #fff;
font-family: "Helvetica Neue", sans-serif;
font-size: 1rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 1rem;
}
.heading {

View File

@ -1,3 +1,3 @@
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.7056 8.36093C25.3565 7.45862 24.8532 6.64096 24.2237 5.95372C23.5938 5.26443 22.851 4.71666 22.0359 4.34019C21.1907 3.94828 20.2842 3.74767 19.369 3.75002C18.085 3.75002 16.8322 4.1425 15.7436 4.88385C15.4831 5.06119 15.2357 5.25598 15.0013 5.46821C14.7669 5.25598 14.5195 5.06119 14.259 4.88385C13.1704 4.1425 11.9176 3.75002 10.6336 3.75002C9.70902 3.75002 8.81308 3.94771 7.96663 4.34019C7.14883 4.71814 6.41176 5.26179 5.77888 5.95372C5.14859 6.64019 4.6451 7.45804 4.29694 8.36093C3.93492 9.29997 3.75 10.2972 3.75 11.3234C3.75 12.2915 3.9271 13.3004 4.27871 14.3266C4.57301 15.1843 4.99493 16.0739 5.53406 16.9722C6.38832 18.3939 7.56294 19.8766 9.02144 21.3796C11.4384 23.8711 13.8319 25.5922 13.9335 25.662L14.5507 26.1039C14.8242 26.2987 15.1758 26.2987 15.4493 26.1039L16.0665 25.662C16.1681 25.5893 18.559 23.8711 20.9785 21.3796C22.437 19.8766 23.6117 18.3939 24.4659 16.9722C25.005 16.0739 25.4296 15.1843 25.7213 14.3266C26.0729 13.3004 26.25 12.2915 26.25 11.3234C26.2526 10.2972 26.0677 9.29997 25.7056 8.36093ZM15.0013 23.8043C15.0013 23.8043 5.72939 17.1728 5.72939 11.3234C5.72939 8.36093 7.92496 5.95953 10.6336 5.95953C12.5375 5.95953 14.1887 7.14569 15.0013 8.87842C15.8139 7.14569 17.4651 5.95953 19.369 5.95953C22.0776 5.95953 24.2732 8.36093 24.2732 11.3234C24.2732 17.1728 15.0013 23.8043 15.0013 23.8043Z" fill="white"/>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 3C4.239 3 2 5.216 2 7.95C2 10.157 2.875 15.395 11.488 20.69C11.6423 20.7839 11.8194 20.8335 12 20.8335C12.1806 20.8335 12.3577 20.7839 12.512 20.69C21.125 15.395 22 10.157 22 7.95C22 5.216 19.761 3 17 3C14.239 3 12 6 12 6C12 6 9.761 3 7 3Z" stroke="#D94848" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 439 B

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 12H8M12 8V12V8ZM12 12V16V12ZM12 12H16H12Z" stroke="white" stroke-width="2" stroke-linecap="round"/>
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" stroke="white" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 374 B

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 22L3 18L7 14V17H17V13H19V18C19 18.2652 18.8946 18.5196 18.7071 18.7071C18.5196 18.8946 18.2652 19 18 19H7V22ZM7 11H5V6C5 5.73478 5.10536 5.48043 5.29289 5.29289C5.48043 5.10536 5.73478 5 6 5H17V2L21 6L17 10V7H7V11Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 346 B

View File

@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.1666 14.1667H12.8675L4.78413 5.2725C4.70599 5.18663 4.61077 5.11803 4.50458 5.0711C4.39839 5.02418 4.28356 4.99996 4.16746 5H1.66663V6.66667H3.79913L7.20746 10.4167L3.79913 14.1675H1.66663V15.8342H4.16746C4.28356 15.8342 4.39839 15.81 4.50458 15.7631C4.61077 15.7161 4.70599 15.6475 4.78413 15.5617L8.33329 11.6558L11.8825 15.5608C11.9606 15.6467 12.0558 15.7153 12.162 15.7622C12.2682 15.8092 12.383 15.8334 12.4991 15.8333H14.1666V18.3333L18.3333 15L14.1666 11.6667V14.1667V14.1667Z" fill="white"/>
<path d="M12.8675 6.66667H14.1666V9.16667L18.3333 5.885L14.1666 2.5V5H12.4991C12.383 4.99996 12.2682 5.02418 12.162 5.0711C12.0558 5.11803 11.9606 5.18663 11.8825 5.2725L9.07581 8.36167L10.3091 9.48333L12.8675 6.66667Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 852 B

View File

@ -14,7 +14,10 @@
<tr
v-for="song in songs"
:key="song"
@click="updateQueue(song, song.type.name, song.type.id)"
@click="
updateQueue(song, song.type.name, song.type.id),
playAudio(song.filepath)
"
>
<td :style="{ width: songTitleWidth + 'px' }" class="flex">
<div
@ -58,7 +61,7 @@
import { ref, toRefs } from "@vue/reactivity";
import { onMounted, onUnmounted } from "@vue/runtime-core";
import { playAudio } from "@/composables/playAudio.js";
import audio from "@/composables/playAudio.js";
import getQueue from "@/composables/getQueue.js";
import perks from "@/composables/perks.js";
@ -81,28 +84,12 @@ export default {
perks.current.value = song;
localStorage.setItem("current", JSON.stringify(song));
const index = perks.queue.value.findIndex(
(item) => item._id.$oid === song._id.$oid
);
if (index == perks.queue.value.length - 1) {
perks.next.value = perks.queue.value[0];
} else {
perks.next.value = perks.queue.value[index + 1];
}
localStorage.setItem(
"next",
JSON.stringify(perks.queue.value[index + 1])
);
};
const resizeSongTitleWidth = () => {
try {
let a = songtitle.value.clientWidth;
songTitleWidth.value = a > minWidth.value * 4 ? a / 4 : a / 3;
} catch (error) {
return;
@ -123,6 +110,8 @@ export default {
});
});
const playAudio = audio.playAudio;
return {
songtitle,
songTitleWidth,
@ -147,25 +136,6 @@ export default {
}
}
.folder .table table {
border-collapse: collapse;
text-transform: capitalize;
position: relative;
margin: 1rem;
tbody tr {
cursor: pointer;
transition: all 0.5s ease;
&:hover {
td {
background-color: rgba(255, 174, 0, 0.534);
}
transform: scale(0.99);
}
}
}
.folder .table table td .album-art {
width: 3rem;
height: 3rem;
@ -187,6 +157,8 @@ td,
th {
padding: 8px;
text-align: left;
outline: none;
border: none;
}
th {
@ -210,4 +182,23 @@ td .artist {
font-weight: lighter;
margin-right: 0.5rem;
}
.folder .table table {
border-collapse: collapse;
text-transform: capitalize;
position: relative;
margin: 1rem;
tbody tr {
cursor: pointer;
transition: all 0.5s ease;
&:hover {
& {
background-color: rgba(255, 174, 0, 0.534);
}
transform: scale(0.99);
}
}
}
</style>

View File

@ -17,10 +17,23 @@
</div>
</div>
</div>
<div class="progress">
<input type="range" :value="pos" min="0" max="100" />
</div>
<div class="controls">
<div class="image" id="previous"></div>
<div class="image" id="play-pause"></div>
<div class="image" id="next"></div>
<div class="shuffle">
<div class="image"></div>
<div class="image"></div>
</div>
<div class="nav">
<div class="image" id="previous" @click="playPrev"></div>
<div class="image" id="play-pause" @click="playPause"></div>
<div class="image" id="next" @click="playNext"></div>
</div>
<div class="fav">
<div class="image"></div>
<div class="image"></div>
</div>
</div>
</div>
</template>
@ -28,87 +41,191 @@
<script>
import { ref } from "@vue/reactivity";
import perks from "../../composables/perks.js";
import playAudio from "@/composables/playAudio.js";
export default {
setup() {
const current = ref(perks.current);
const putCommas = perks.putCommas;
const pos = playAudio.pos;
return { current, putCommas };
const { playNext } = playAudio;
const { playPrev } = playAudio;
const { playPause } = playAudio;
return { current, putCommas, playNext, playPrev, playPause, pos };
},
};
</script>
<style>
<style lang="scss">
.now-playing {
height: 5rem;
border-radius: 0.5rem;
margin-top: 1rem;
padding: 0.5rem;
background-color: #131313b2;
background-color: rgb(12, 12, 12);
display: grid;
grid-template-columns: 3fr 1fr;
}
grid-template-rows: 3fr 1fr;
.now-playing .art-tags {
display: flex;
align-items: center;
}
.progress {
display: flex;
align-items: center;
height: 1.5rem;
.now-playing .art-tags .album-art {
width: 4.5rem;
height: 4.5rem;
border-radius: 0.5rem;
margin-right: 0.5rem;
background-color: #ad1717a8;
background-image: url(../../assets/images/null.webp);
}
input {
-webkit-appearance: none;
width: 100%;
border: none;
outline: none;
background: transparent;
}
.now-playing .art-tags hr {
border: none;
margin: 0.3rem;
}
.now-playing .art-tags #title {
margin: 0;
width: 14rem;
color: #fff;
}
input:focus {
outline: none;
}
.now-playing .art-tags #artist {
font-weight: lighter;
font-size: small;
color: rgba(255, 255, 255, 0.712);
}
input::-webkit-slider-runnable-track {
width: 100%;
height: 0.25rem;
cursor: pointer;
background: #3071a9;
}
.now-playing .controls {
display: grid;
grid-template-columns: repeat(3, 1fr);
align-items: center;
}
input::-webkit-slider-thumb {
-webkit-appearance: none;
height: 1rem;
width: 1rem;
border-radius: 50%;
background: #ffffff;
cursor: pointer;
margin-top: -0.35rem;
}
.now-playing .controls * {
height: 3rem;
width: 3rem;
background-size: 50%;
cursor: pointer;
border-radius: 0.5rem;
}
input:focus::-webkit-slider-runnable-track,
input::-moz-range-track {
background: #367ebd;
}
.now-playing .controls *:hover {
filter: invert(66%) sepia(75%) saturate(4335%) hue-rotate(158deg)
brightness(89%) contrast(101%);
}
input::-moz-range-thumb {
height: 1rem;
width: 1rem;
border-radius: 50%;
background: #ffffff;
cursor: pointer;
margin-top: -0.35rem;
}
}
.now-playing .controls #previous {
background-image: url(../../assets/icons/previous.svg);
}
.controls {
display: grid;
grid-template-columns: repeat(3, 1fr);
.now-playing .controls #play-pause {
background-image: url(../../assets/icons/pause.svg);
}
.nav {
display: grid;
grid-template-columns: repeat(3, 1fr);
width: 100%;
.now-playing .controls #next {
background-image: url(../../assets/icons/next.svg);
& * {
height: 3rem;
width: 3rem;
background-size: 50%;
cursor: pointer;
border-radius: 0.5rem;
}
& *:hover {
filter: invert(66%) sepia(75%) saturate(4335%) hue-rotate(158deg)
brightness(89%) contrast(101%);
}
#previous {
background-image: url(../../assets/icons/previous.svg);
}
#play-pause {
background-image: url(../../assets/icons/pause.svg);
}
#next {
background-image: url(../../assets/icons/next.svg);
}
}
.shuffle {
width: 100%;
display: flex;
align-items: center;
& * {
height: 2rem;
width: 2rem;
background-size: 70%;
cursor: pointer;
border-radius: 0.5rem;
}
& :first-child {
background-image: url(../../assets/icons/repeat.svg);
}
& :last-child {
background-image: url(../../assets/icons/shuffle.svg);
}
}
.fav {
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
& * {
height: 2rem;
width: 2rem;
background-size: 70%;
border-radius: 0.5rem;
cursor: pointer;
}
& :first-child {
background-image: url(../../assets/icons/plus.svg);
}
& :last-child {
background-image: url(../../assets/icons/heart.svg);
}
}
}
.art-tags {
display: flex;
align-items: center;
hr {
border: none;
margin: 0.3rem;
}
#title {
margin: 0;
width: 13rem;
color: #fff;
}
#artist {
font-size: small;
color: rgba(255, 255, 255, 0.712);
}
.album-art {
width: 6rem;
height: 6rem;
border-radius: 0.5rem;
margin-right: 0.5rem;
margin-left: $small;
background-color: #ad1717a8;
background-image: url(../../assets/images/null.webp);
}
}
}
</style>

View File

@ -3,7 +3,7 @@
<p class="heading">
COMING UP NEXT <span class="more" @click="collapse">SEE ALL</span>
</p>
<div class="main-item h-1">
<div class="main-item h-1" @click="playNext">
<div
class="album-art image"
:style="{
@ -23,7 +23,7 @@
<div>
<div :class="{ hr: is_expanded }" class="all-items">
<div :class="{ v0: !is_expanded, v1: is_expanded }" class="scrollable">
<div class="song-item h-1" v-for="song in queue" :key="song">
<div class="song-item h-1" v-for="song in queue" :key="song" @click="playThis(song)">
<div
class="album-art image"
:style="{
@ -49,23 +49,30 @@
<script>
import { ref, toRefs } from "@vue/reactivity";
import perks from "@/composables/perks.js";
import audio from "@/composables/playAudio.js";
export default {
props: ["up_next"],
setup(props, { emit }) {
const is_expanded = toRefs(props).up_next;
const queue = ref(perks.queue);
const next = ref(perks.next);
let collapse = () => {
emit("expandQueue");
};
const { playNext } = audio;
const {playAudio} = audio;
const playThis = (song) => {
playAudio(song.filepath);
perks.current.value = song;
}
const queue = ref(perks.queue);
const next = ref(perks.next);
const putCommas = perks.putCommas;
return { collapse, is_expanded, putCommas, queue, next };
return { collapse, is_expanded, playNext, playThis, putCommas, queue, next };
},
};
</script>

View File

@ -1,23 +1,38 @@
import { ref } from "@vue/reactivity";
import { watch } from "@vue/runtime-core";
const current = ref({
title: "Nothing played yet",
artists: ["... blah blah blah"],
title: "Nothing played yet",
artists: ["... blah blah blah"],
_id: {
$oid: "",
},
});
const next = ref({
title: "Next song shows here",
artists: ["... blah blah blah"],
title: "The next song",
artists: ["... blah blah blah"],
_id: {
$oid: "",
},
});
const prev = ref({
title: "The previous song",
artists: ["... blah blah blah"],
_id: {
$oid: "",
},
});
const queue = ref([
{
title: "Nothing played yet",
artists: ["... blah blah blah"],
_id: {
$oid: ""
}
}
{
title: "Nothing played yet",
artists: ["... blah blah blah"],
_id: {
$oid: "",
},
},
]);
const putCommas = (artists) => {
@ -42,21 +57,40 @@ const doThat = (songs, current) => {
};
const readQueue = () => {
const prev_queue = JSON.parse(localStorage.getItem("queue"));
const last_played = JSON.parse(localStorage.getItem("current"));
const next_ = JSON.parse(localStorage.getItem("next"));
const prev_queue = JSON.parse(localStorage.getItem("queue"));
const last_played = JSON.parse(localStorage.getItem("current"));
const next_ = JSON.parse(localStorage.getItem("next"));
if (last_played){
current.value = last_played;
}
if (last_played) {
current.value = last_played;
}
if (prev_queue){
queue.value = prev_queue;
}
if (prev_queue) {
queue.value = prev_queue;
}
if (next_){
next.value = next_;
}
}
if (next_) {
next.value = next_;
}
};
export default { putCommas, doThat, readQueue, current, queue, next };
watch(current, (new_current) => {
localStorage.setItem("current", JSON.stringify(new_current));
const index = queue.value.findIndex(
(item) => item._id.$oid === new_current._id.$oid
);
if (index == queue.value.length - 1) {
next.value = queue.value[0];
prev.value = queue.value[queue.value.length - 2];
} else if (index == 0) {
next.value = queue.value[1];
prev.value = queue.value[queue.value.length - 1];
} else {
next.value = queue.value[index + 1];
prev.value = queue.value[index - 1];
}
});
export default { putCommas, doThat, readQueue, current, queue, next, prev };

View File

@ -1,14 +1,44 @@
import { ref } from "@vue/reactivity";
import perks from "./perks";
const audio = ref(new Audio()).value;
const pos = ref(0);
const url = "http://127.0.0.1:8901/";
const playAudio = (path) => {
const full_path = "http://127.0.0.1:8901/" + encodeURIComponent(path);
const full_path = url + encodeURIComponent(path);
audio.src = full_path;
audio.addEventListener("canplaythrough", () => {
audio.play();
audio.play();
audio.ontimeupdate = () => {
pos.value = audio.currentTime / audio.duration * 100;
}
audio.addEventListener("ended", () => {
playNext();
});
};
export { playAudio };
function playNext() {
playAudio(perks.next.value.filepath);
perks.current.value = perks.next.value;
}
function playPrev() {
playAudio(perks.prev.value.filepath);
perks.current.value = perks.prev.value;
}
function playPause() {
if (audio.paused) {
audio.play();
} else {
audio.pause();
}
}
export default { playAudio, playNext, playPrev, playPause, pos };

View File

@ -19,6 +19,10 @@ const routes = [
name: "FolderView",
component: FolderView,
},
{
path: "/folder/",
redirect: "/folder/home",
},
{
path: "/playlist",
name: "PlaylistView",