Added real frontend
Made hostname global and easy to change in docker
This commit is contained in:
1 parent
abdce7b103
commit
e5b4d2c3f3
56 files changed
+31491
-27034
No files matched your search
@@ -0,0 +1,371 @@
|
||||
<template>
|
||||
<footer>
|
||||
<div class="audioAndButtonsDiv">
|
||||
<div class="audioButtonsDiv">
|
||||
<button
|
||||
class="bigButton"
|
||||
:class="{ disabled: disableButtons }"
|
||||
:disabled="disableButtons"
|
||||
@click="randomizeTrack()"
|
||||
>
|
||||
<p v-if="!disableButtons"><u>R</u>andomize new track</p>
|
||||
<p v-else>Loading...</p>
|
||||
</button>
|
||||
<button class="bigButton" @click="showAnswer()"><u>S</u>how answer</button>
|
||||
</div>
|
||||
<div class="audioDiv">
|
||||
<audio
|
||||
controls
|
||||
class="audioPlayer"
|
||||
:src="currentTrackSrcFile"
|
||||
ref="audioPlayer"
|
||||
:onended="trackHasEnded"
|
||||
></audio>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import arne from "../../arne.js";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
currentTrackSrcFile: "",
|
||||
disableButtons: false,
|
||||
loadingBar: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState([
|
||||
"stopAfterCurrent",
|
||||
"localPlaylist",
|
||||
"hideNextTrack",
|
||||
"roundStarted",
|
||||
"specialTrackIsPlaying",
|
||||
]),
|
||||
},
|
||||
methods: {
|
||||
async randomizeTrack() {
|
||||
console.log("Randomizing track");
|
||||
/* Prevents anyone from changing the winning score after the round has started */
|
||||
if (this.roundStarted === false) {
|
||||
this.$store.dispatch("setRoundStarted", true);
|
||||
}
|
||||
let trackAddedToQue = false;
|
||||
this.disableButtons = true;
|
||||
let copyOfPlaylist = this.localPlaylist;
|
||||
//If the local playlist of MP3s is empty, wait for a new track to load
|
||||
if (copyOfPlaylist.length === 0) {
|
||||
console.log("No track preloaded, loading a new one");
|
||||
await this.APIgetRandomizedTrack();
|
||||
await this.APIaddToQue();
|
||||
trackAddedToQue = true;
|
||||
}
|
||||
//Update the source file for the media player (binded property to Audio tag in the template)
|
||||
this.currentTrackSrcFile = window.URL.createObjectURL(copyOfPlaylist[0]);
|
||||
/* this.$nextTick(() => {}); */
|
||||
if (!trackAddedToQue) {
|
||||
await this.APIaddToQue();
|
||||
}
|
||||
await this.APIgetPlaylistHistory();
|
||||
|
||||
this.$refs.audioPlayer.play();
|
||||
this.$emit("scroll-to-bottom-playlist-history");
|
||||
copyOfPlaylist.shift();
|
||||
if (this.hideNextTrack) {
|
||||
this.$store.dispatch("setCurrentTrackHidden", true);
|
||||
} else {
|
||||
this.$store.dispatch("setCurrentTrackHidden", false);
|
||||
}
|
||||
await this.APIgetInfoAboutCurrentTrack();
|
||||
await this.randomizeTrackInAdvance();
|
||||
this.disableButtons = false;
|
||||
},
|
||||
async randomizeTrackInAdvance() {
|
||||
console.log("Randomizing track in advance");
|
||||
try {
|
||||
await this.APIgetRandomizedTrack();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.log("Error: A track couldn't be loaded in advance");
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
async playSpecificTrack(track) {
|
||||
await this.APIplaySpecificTrack(track.SongNo);
|
||||
this.$store.dispatch("setCurrentlyLoadingTrack", "");
|
||||
await this.APIgetPlaylistHistory();
|
||||
this.$store.dispatch("setCurrentTrackHidden", false);
|
||||
await this.APIgetInfoAboutCurrentTrack();
|
||||
},
|
||||
showAnswer() {
|
||||
this.$store.dispatch("setCurrentTrackHidden", false);
|
||||
},
|
||||
startSoundTest() {
|
||||
this.$refs.audioPlayer.pause();
|
||||
this.currentTrackSrcFile = "sounds/check.mp3";
|
||||
this.$store.dispatch("setSpecialTrackIsPlaying", true);
|
||||
this.$nextTick(() => {
|
||||
this.$refs.audioPlayer.currentTime = 0;
|
||||
this.$refs.audioPlayer.play();
|
||||
});
|
||||
},
|
||||
playWelcomeSound() {
|
||||
let randomNumber = Math.floor(Math.random() * 10);
|
||||
this.$refs.audioPlayer.pause();
|
||||
if (randomNumber == 0) {
|
||||
this.currentTrackSrcFile = "sounds/sound1.mp3";
|
||||
} else if (randomNumber < 5) {
|
||||
this.currentTrackSrcFile = "sounds/intro_short.mp3";
|
||||
} else {
|
||||
this.currentTrackSrcFile = "sounds/intro_long.mp3";
|
||||
}
|
||||
this.$store.dispatch("setSpecialTrackIsPlaying", true);
|
||||
this.$nextTick(() => {
|
||||
this.$refs.audioPlayer.currentTime = 0;
|
||||
this.$refs.audioPlayer.play();
|
||||
});
|
||||
},
|
||||
playWinningSound() {
|
||||
this.$refs.audioPlayer.pause();
|
||||
this.currentTrackSrcFile = "sounds/winning.mp3";
|
||||
this.$store.dispatch("setSpecialTrackIsPlaying", true);
|
||||
this.$nextTick(() => {
|
||||
this.$refs.audioPlayer.currentTime = 0;
|
||||
this.$refs.audioPlayer.play();
|
||||
});
|
||||
},
|
||||
APIgetRandomizedTrack() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.axios({
|
||||
method: "get",
|
||||
url: `${arne.hostname}/music/rand`,
|
||||
responseType: "blob",
|
||||
onDownloadProgress: (progressEvent) => {
|
||||
let percentCompleted = Math.round(
|
||||
(progressEvent.loaded * 100) / progressEvent.total
|
||||
);
|
||||
if (percentCompleted % 10 == 0) {
|
||||
//Debug for slow downloading
|
||||
//console.log(percentCompleted);
|
||||
}
|
||||
},
|
||||
})
|
||||
.then((response) => {
|
||||
let mp3 = new Blob([response.data], { type: "audio/mp3" });
|
||||
this.$store.dispatch("updateLocalPlaylist", mp3);
|
||||
this.$nextTick(() => {
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log("An error happened when fetching the track");
|
||||
console.log(error);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
},
|
||||
APIaddToQue() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.axios({
|
||||
method: "get",
|
||||
url: `${arne.hostname}/music/addQue`,
|
||||
})
|
||||
.then(() => {
|
||||
resolve(false);
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
},
|
||||
APIgetInfoAboutCurrentTrack() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.axios({
|
||||
method: "get",
|
||||
url: `${arne.hostname}/music/info`,
|
||||
})
|
||||
.then((response) => {
|
||||
let gameInfoObject = {
|
||||
game: response.data.Game,
|
||||
track: response.data.Song.replace(".mp3", ""),
|
||||
songNo: response.data.SongNo,
|
||||
};
|
||||
this.$store.dispatch("setCurrentGame", gameInfoObject.game);
|
||||
this.$store.dispatch("setCurrentTrack", gameInfoObject.track);
|
||||
resolve(false);
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
},
|
||||
APIgetPlaylistHistory() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.axios({
|
||||
method: "get",
|
||||
url: `${arne.hostname}/music/list`,
|
||||
})
|
||||
.then((response) => {
|
||||
let tracklistArray = response.data;
|
||||
this.$store.dispatch("updatePlaylistHistory", tracklistArray);
|
||||
resolve();
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
},
|
||||
APIplaySpecificTrack(trackNumber) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.axios({
|
||||
method: "get",
|
||||
url: `${arne.hostname}/music?song=${trackNumber}`,
|
||||
responseType: "blob",
|
||||
})
|
||||
.then((response) => {
|
||||
let mp3 = new Blob([response.data], { type: "audio/mp3" });
|
||||
this.currentTrackSrcFile = window.URL.createObjectURL(mp3);
|
||||
this.$nextTick(() => {
|
||||
this.$refs.audioPlayer.play();
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
},
|
||||
trackHasEnded() {
|
||||
if (this.specialTrackIsPlaying) {
|
||||
this.$store.dispatch("setSpecialTrackIsPlaying", false);
|
||||
return 0;
|
||||
}
|
||||
if (!this.stopAfterCurrent) {
|
||||
this.randomizeTrack();
|
||||
}
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
await this.APIgetPlaylistHistory();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
footer {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.audioAndButtonsDiv {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 80vw;
|
||||
margin-right: 1vh;
|
||||
}
|
||||
.audioButtonsDiv {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
width: 100%;
|
||||
margin-bottom: 2vh;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.audioButtonsDiv button:nth-child(1) {
|
||||
margin-right: 3vw;
|
||||
width: 200px;
|
||||
}
|
||||
.audioButtonsDiv button:nth-child(2) {
|
||||
margin-right: 25vw;
|
||||
width: 220px;
|
||||
}
|
||||
.audioButtonsDiv .bigButton {
|
||||
height: 7vh;
|
||||
}
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.audioDiv {
|
||||
width: 100%;
|
||||
}
|
||||
.audioDiv audio {
|
||||
width: 100%;
|
||||
font-size: 2rem;
|
||||
}
|
||||
audio::-webkit-media-controls-panel {
|
||||
background-color: rgb(185, 185, 185);
|
||||
}
|
||||
audio::-webkit-media-controls-current-time-display {
|
||||
color: rgb(0, 0, 0);
|
||||
font-size: 4em;
|
||||
}
|
||||
audio::-webkit-media-controls-time-remaining-display {
|
||||
color: rgb(0, 0, 0);
|
||||
font-size: 4em;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px) {
|
||||
footer {
|
||||
position: static;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.audioAndButtonsDiv {
|
||||
width: 100vw;
|
||||
margin: 0;
|
||||
}
|
||||
.audioButtonsDiv {
|
||||
margin-bottom: 2vw;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.audioButtonsDiv button:nth-child(1) {
|
||||
margin: 0;
|
||||
width: 50vw;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.audioButtonsDiv button:nth-child(2) {
|
||||
margin: 0;
|
||||
width: 50vw;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.audioButtonsDiv .bigButton {
|
||||
height: 22vw;
|
||||
}
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.audioDiv {
|
||||
width: 100%;
|
||||
}
|
||||
.audioDiv audio {
|
||||
font-size: 1rem;
|
||||
}
|
||||
audio::-webkit-media-controls-panel {
|
||||
background-color: rgb(185, 185, 185);
|
||||
border-radius: 2px;
|
||||
}
|
||||
audio::-webkit-media-controls-current-time-display {
|
||||
color: rgb(0, 0, 0);
|
||||
font-size: 2em;
|
||||
}
|
||||
audio::-webkit-media-controls-time-remaining-display {
|
||||
color: rgb(0, 0, 0);
|
||||
font-size: 2em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<nav>
|
||||
<p class="title">Music Player Randomizer</p>
|
||||
<div class="navMenuWrapper">
|
||||
<button class="settingsButton" @click="showSettingsModal">
|
||||
Settings
|
||||
</button>
|
||||
<button class="statisticsButton" @click="showStatisticsModal">
|
||||
Statistics
|
||||
</button>
|
||||
<button class="aboutButton" @click="showAboutModal">About</button>
|
||||
</div>
|
||||
<p v-if="!$parent.displayWhenDesktop" class="mobileMenuButton" @click="showMobileMenu">
|
||||
Menu
|
||||
</p>
|
||||
<settingsModal ref="settingsModal"></settingsModal>
|
||||
<statisticsModal ref="statisticsModal"></statisticsModal>
|
||||
<aboutModal ref="aboutModal"></aboutModal>
|
||||
<mobileMenu
|
||||
ref="mobileMenu"
|
||||
@start-sound-test="startSoundTest"
|
||||
@show-settings-modal="showSettingsModal"
|
||||
@show-statistics-modal="showStatisticsModal"
|
||||
@show-about-modal="showAboutModal"
|
||||
></mobileMenu>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import aboutModal from "../items/aboutModal.vue";
|
||||
import statisticsModal from "../items/statisticsModal.vue";
|
||||
import settingsModal from "../items/settingsModal.vue";
|
||||
import mobileMenu from "../items/mobileMenu.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
aboutModal,
|
||||
settingsModal,
|
||||
statisticsModal,
|
||||
mobileMenu,
|
||||
},
|
||||
methods: {
|
||||
playSelectedTrack(track) {
|
||||
this.$refs.theFooter.playSpecificTrack(track);
|
||||
},
|
||||
scrollToBottomPlaylistHistory() {
|
||||
this.$refs.playlistHistory.scrollToBottom();
|
||||
},
|
||||
showAboutModal() {
|
||||
this.$refs.aboutModal.openModal();
|
||||
},
|
||||
showSettingsModal() {
|
||||
this.$refs.settingsModal.openModal();
|
||||
},
|
||||
showStatisticsModal() {
|
||||
this.$refs.statisticsModal.openModal();
|
||||
},
|
||||
showMobileMenu() {
|
||||
this.$refs.mobileMenu.openMobileMenu();
|
||||
this.showMobileMenuContent();
|
||||
},
|
||||
showMobileMenuContent() {
|
||||
setTimeout(() => {
|
||||
this.$refs.mobileMenu.openMobileMenuContent();
|
||||
}, 20);
|
||||
},
|
||||
startSoundTest() {
|
||||
this.$emit("start-sound-test");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
nav {
|
||||
background: #333;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 5vh;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 2rem;
|
||||
margin-left: 2vw;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.navMenuWrapper {
|
||||
margin-left: auto;
|
||||
height: 5vh;
|
||||
}
|
||||
|
||||
.navMenuWrapper button {
|
||||
background: #333;
|
||||
color: #ff9c00;
|
||||
font-size: 1.3rem;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
height: 100%;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.navMenuWrapper button:hover {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media only screen and (max-width: 1000px) {
|
||||
nav {
|
||||
justify-content: center;
|
||||
height: 10vw;
|
||||
}
|
||||
.title {
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
margin-left: 5px;
|
||||
/* text-align: ; */
|
||||
}
|
||||
|
||||
.navMenuWrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobileMenuButton {
|
||||
color: white;
|
||||
background: grey;
|
||||
padding: 6px;
|
||||
border-top-left-radius: 7px;
|
||||
border-bottom-left-radius: 7px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div class="theMainDiv">
|
||||
<div class="mainFirstRow">
|
||||
<currently-playing></currently-playing>
|
||||
<inspiration-window></inspiration-window>
|
||||
</div>
|
||||
<div class="mainSecondRow">
|
||||
<playlist-history
|
||||
ref="playlistHistory"
|
||||
@play-selected-track="playSelectedTrack"
|
||||
></playlist-history>
|
||||
<players-window
|
||||
@play-welcome-sound="playWelcomeSound"
|
||||
@play-winning-sound="playWinningSound"
|
||||
></players-window>
|
||||
</div>
|
||||
<extra-buttons
|
||||
v-if="$parent.displayWhenDesktop"
|
||||
@start-sound-test="startSoundTest"
|
||||
></extra-buttons>
|
||||
<the-footer
|
||||
ref="theFooter"
|
||||
@scroll-to-bottom-playlist-history="scrollToBottomPlaylistHistory"
|
||||
></the-footer>
|
||||
<!-- <the-footer ref="theFooter"></the-footer> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import currentlyPlaying from "../items/CurrentlyPlaying.vue";
|
||||
import inspirationWindow from "../items/InspirationWindow.vue";
|
||||
import playersWindow from "../items/playersWindow.vue";
|
||||
import playlistHistory from "../items/playlistHistory.vue";
|
||||
import extraButtons from "../items/extraButtons.vue";
|
||||
import theFooter from "./TheFooter.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
currentlyPlaying,
|
||||
playersWindow,
|
||||
playlistHistory,
|
||||
inspirationWindow,
|
||||
extraButtons,
|
||||
theFooter,
|
||||
},
|
||||
methods: {
|
||||
playSelectedTrack(track) {
|
||||
this.$refs.theFooter.playSpecificTrack(track);
|
||||
},
|
||||
scrollToBottomPlaylistHistory() {
|
||||
this.$refs.playlistHistory.scrollToBottom();
|
||||
},
|
||||
startSoundTest() {
|
||||
this.$refs.theFooter.startSoundTest();
|
||||
},
|
||||
playWelcomeSound() {
|
||||
this.$refs.theFooter.playWelcomeSound();
|
||||
},
|
||||
playWinningSound() {
|
||||
this.$refs.theFooter.playWinningSound();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.theMainDiv {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mainFirstRow {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
width: 100%;
|
||||
margin: 0.5vw;
|
||||
}
|
||||
.mainSecondRow {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
width: 100%;
|
||||
margin: 0 0.5vw;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px) {
|
||||
.mainFirstRow {
|
||||
margin: 0;
|
||||
margin-top: 0.5vw;
|
||||
}
|
||||
.mainSecondRow {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
margin-top: 0.5vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user