almost complete player

This commit is contained in:
2024-06-25 17:59:01 +02:00
parent b2d8180bb9
commit ed63ca77d6
6 changed files with 482 additions and 50 deletions

View File

@@ -1,26 +1,39 @@
<template>
<div>
<div :class="'player' + ( isShowingFullScreenPlayer ? '' : ' player-hidden' )">
<!-- TODO: Make cover art of song or otherwise MusicPlayer Logo -->
<img src="https://github.com/simplePCBuilding/MusicPlayerV2/raw/master/assets/logo.png" alt="MusicPlayer Logo" class="logo-player" @click="controlUI( 'show' )" v-if="coverArt === ''">
<img :src="coverArt" alt="MusicPlayer Logo" class="logo-player" @click="controlUI( 'show' )" v-else>
<p class="song-name" @click="controlUI( 'show' )">{{ currentlyPlayingSongName }}</p>
<p>{{ nicePlaybackPos }} / {{ niceDuration }}</p>
<div class="controls-wrapper">
<span class="material-symbols-outlined controls next-previous" @click="control( 'previous' )" id="previous">skip_previous</span>
<span class="material-symbols-outlined controls forward-back" @click="control( 'back' )" :style="'rotate: -' + 360 * clickCountBack + 'deg;'">replay_10</span>
<span class="material-symbols-outlined controls" v-if="isPlaying" @click="playPause()" id="play-pause">pause</span>
<span class="material-symbols-outlined controls" v-else @click="playPause()" id="play-pause">play_arrow</span>
<span class="material-symbols-outlined controls forward-back" @click="control( 'forward' )" :style="'rotate: ' + 360 * clickCountForward + 'deg;'">forward_10</span>
<span class="material-symbols-outlined controls next-previous" @click="control( 'next' )" id="next">skip_next</span>
<span class="material-symbols-outlined controls" @click="control( 'repeat' )" style="margin-left: 20px;">repeat{{ repeatMode }}</span>
<span class="material-symbols-outlined controls" @click="control( 'shuffle' )">shuffle{{ shuffleMode }}</span>
<div class="main-player">
<!-- TODO: Make cover art of song or otherwise MusicPlayer Logo -->
<img src="https://github.com/simplePCBuilding/MusicPlayerV2/raw/master/assets/logo.png" alt="MusicPlayer Logo" class="logo-player" @click="controlUI( 'show' )" v-if="coverArt === ''">
<img :src="coverArt" alt="MusicPlayer Logo" class="logo-player" @click="controlUI( 'show' )" v-else>
<div class="song-name-wrapper">
<p class="song-name" @click="controlUI( 'show' )">{{ currentlyPlayingSongName }} <i v-if="currentlyPlayingSongArtist">by {{ currentlyPlayingSongArtist }}</i></p>
<div :class="'playback' + ( isShowingFullScreenPlayer ? ' full-screen' : '' )">
<sliderView :position="pos" :active="true" :duration="duration" name="main" @pos="( pos ) => player.goToPos( pos )"
v-if="isShowingFullScreenPlayer"></sliderView>
<div :class="'playback-pos-wrapper' + ( isShowingFullScreenPlayer ? ' full-screen' : '' )">
<p class="playback-pos">{{ nicePlaybackPos }}</p>
<p v-if="!isShowingFullScreenPlayer"> / </p>
<p class="playback-duration">{{ niceDuration }}</p>
</div>
</div>
</div>
<div class="controls-wrapper">
<span class="material-symbols-outlined controls next-previous" @click="control( 'previous' )" id="previous">skip_previous</span>
<span class="material-symbols-outlined controls forward-back" @click="control( 'back' )" :style="'rotate: -' + 360 * clickCountBack + 'deg;'">replay_10</span>
<span class="material-symbols-outlined controls" v-if="isPlaying" @click="playPause()" id="play-pause">pause</span>
<span class="material-symbols-outlined controls" v-else @click="playPause()" id="play-pause">play_arrow</span>
<span class="material-symbols-outlined controls forward-back" @click="control( 'forward' )" :style="'rotate: ' + 360 * clickCountForward + 'deg;'">forward_10</span>
<span class="material-symbols-outlined controls next-previous" @click="control( 'next' )" id="next">skip_next</span>
<span class="material-symbols-outlined controls" @click="control( 'repeat' )" style="margin-left: 20px;">repeat{{ repeatMode }}</span>
<span class="material-symbols-outlined controls" @click="control( 'shuffle' )">shuffle{{ shuffleMode }}</span>
</div>
</div>
</div>
<div :class="'playlist-view' + ( isShowingFullScreenPlayer ? '' : ' hidden' )">
<span class="material-symbols-outlined close-fullscreen" @click="controlUI( 'hide' )">close</span>
<playlistView :playlist="playlist" class="pl-wrapper"></playlistView>
<playlistView :playlist="playlist" class="pl-wrapper" :currently-playing="currentlyPlayingSongIndex" :is-playing="isPlaying"
@control="( action ) => { control( action ) }" @play-song="( song ) => { playSong( song ) }"></playlistView>
</div>
</div>
</template>
@@ -32,27 +45,31 @@
import { ref, type Ref } from 'vue';
import playlistView from '@/components/playlistView.vue';
import MusicKitJSWrapper from '@/scripts/music-player';
import sliderView from './sliderView.vue';
import type { Song } from '@/scripts/song';
const isPlaying = ref( false );
const repeatMode = ref( '' );
const shuffleMode = ref( '' );
const currentlyPlayingSongName = ref( 'Not playing' );
const currentlyPlayingSongIndex = ref( 0 );
const clickCountForward = ref( 0 );
const clickCountBack = ref( 0 );
const isShowingFullScreenPlayer = ref( false );
const player = new MusicKitJSWrapper();
const playlist: Ref<Song[]> = ref( [] );
const coverArt = ref( '' );
const nicePlaybackPos = ref( '' );
const niceDuration = ref( '' );
const nicePlaybackPos = ref( '00:00' );
const niceDuration = ref( '00:00' );
const isShowingRemainingTime = ref( false );
const currentlyPlayingSongArtist = ref( '' );
const pos = ref( 0 );
const duration = ref( 0 );
const emits = defineEmits( [ 'playerStateChange' ] );
const playPause = () => {
isPlaying.value = !isPlaying.value;
// TODO: Execute function on player
if ( isPlaying.value ) {
player.control( 'play' );
startProgressTracker();
@@ -63,7 +80,15 @@
}
const control = ( action: string ) => {
if ( action === 'repeat' ) {
if ( action === 'pause' ) {
isPlaying.value = false;
player.control( 'pause' );
stopProgressTracker();
} else if ( action === 'play' ) {
isPlaying.value = true;
player.control( 'play' );
startProgressTracker();
} else if ( action === 'repeat' ) {
if ( repeatMode.value === '' ) {
repeatMode.value = '_on';
player.setRepeatMode( 'all' );
@@ -82,6 +107,7 @@
shuffleMode.value = '';
player.setShuffle( false );
}
getDetails();
} else if ( action === 'forward' ) {
clickCountForward.value += 1;
player.control( 'skip-10' );
@@ -91,6 +117,8 @@
} else if ( action === 'next' ) {
stopProgressTracker();
player.control( 'next' );
coverArt.value = '';
currentlyPlayingSongArtist.value = '';
currentlyPlayingSongName.value = 'Loading...';
setTimeout( () => {
getDetails();
@@ -99,6 +127,8 @@
} else if ( action === 'previous' ) {
stopProgressTracker();
player.control( 'previous' );
coverArt.value = '';
currentlyPlayingSongArtist.value = '';
currentlyPlayingSongName.value = 'Loading...';
setTimeout( () => {
getDetails();
@@ -135,6 +165,8 @@
}
const selectPlaylist = ( id: string ) => {
currentlyPlayingSongArtist.value = '';
coverArt.value = '';
currentlyPlayingSongName.value = 'Loading...';
player.setPlaylistByID( id ).then( () => {
isPlaying.value = true;
@@ -149,14 +181,36 @@
const details = player.getPlayingSong();
currentlyPlayingSongName.value = details.title;
coverArt.value = details.cover;
// console.log( player.getQueue() );
playlist.value = player.getPlaylist();
currentlyPlayingSongIndex.value = player.getPlayingSongID();
playlist.value = player.getQueue();
console.log( playlist.value );
currentlyPlayingSongArtist.value = details.artist;
}
const playSong = ( id: string ) => {
const p = player.getPlaylist();
currentlyPlayingSongArtist.value = '';
coverArt.value = '';
currentlyPlayingSongName.value = 'Loading...';
stopProgressTracker();
for ( const s in p ) {
if ( p[ s ].id === id ) {
player.prepare( parseInt( s ) );
setTimeout( () => {
getDetails();
startProgressTracker();
}, 2000 );
break;
}
}
}
let progressTracker = 0;
const startProgressTracker = () => {
isPlaying.value = true;
const playingSong = player.getPlayingSong();
duration.value = playingSong.duration;
const minuteCounts = Math.floor( ( playingSong.duration ) / 60 );
niceDuration.value = String( minuteCounts ) + ':';
if ( ( '' + minuteCounts ).length === 1 ) {
@@ -169,17 +223,18 @@
niceDuration.value += secondCounts;
}
progressTracker = setInterval( () => {
const pos = player.getPlaybackPos();
if ( pos > playingSong.duration - 1 ) {
pos.value = player.getPlaybackPos();
if ( pos.value > playingSong.duration - 1 ) {
// TODO: repeat
control( 'next' );
}
const minuteCount = Math.floor( pos / 60 );
const minuteCount = Math.floor( pos.value / 60 );
nicePlaybackPos.value = minuteCount + ':';
if ( ( '' + minuteCount ).length === 1 ) {
nicePlaybackPos.value = '0' + minuteCount + ':';
}
const secondCount = Math.floor( pos - minuteCount * 60 );
const secondCount = Math.floor( pos.value - minuteCount * 60 );
if ( ( '' + secondCount ).length === 1 ) {
nicePlaybackPos.value += '0' + secondCount;
} else {
@@ -187,12 +242,12 @@
}
if ( isShowingRemainingTime.value ) {
const minuteCounts = Math.floor( ( playingSong.duration - pos ) / 60 );
const minuteCounts = Math.floor( ( playingSong.duration - pos.value ) / 60 );
niceDuration.value = '-' + String( minuteCounts ) + ':';
if ( ( '' + minuteCounts ).length === 1 ) {
niceDuration.value = '-0' + minuteCounts + ':';
}
const secondCounts = Math.floor( ( playingSong.duration - pos ) - minuteCounts * 60 );
const secondCounts = Math.floor( ( playingSong.duration - pos.value ) - minuteCounts * 60 );
if ( ( '' + secondCounts ).length === 1 ) {
niceDuration.value += '0' + secondCounts;
} else {
@@ -206,6 +261,7 @@
try {
clearInterval( progressTracker );
} catch ( _ ) { /* empty */ }
isPlaying.value = false;
}
defineExpose( {
@@ -229,7 +285,18 @@
transition: all 1s;
}
.song-name {
.main-player {
height: 12vh;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: row;
transition: all 1s;
position: relative
}
.song-name-wrapper {
cursor: pointer;
margin-left: 10px;
width: 100%;
@@ -238,8 +305,12 @@
font-weight: bold;
font-size: 1.25rem;
display: flex;
align-items: center;
flex-direction: row;
flex-direction: column;
justify-content: center;
}
.song-name {
margin: 0;
}
.logo-player {
@@ -248,10 +319,6 @@
margin-left: 30px;
}
.playlist-view {
overflow: scroll;
}
.player-hidden {
height: 100%;
}
@@ -325,4 +392,37 @@
.pl-wrapper {
height: 80vh;
}
.playback {
width: fit-content;
bottom: -20px;
left: 7%;
font-weight: normal;
font-size: 1rem;
}
.playback.full-screen {
left: 30%;
position: absolute;
width: 40%;
}
.playback-pos-wrapper {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.playback-pos-wrapper p {
margin: 0;
}
.playback-pos-wrapper.full-screen p {
margin-bottom: 15px;
}
.playback-pos-wrapper.full-screen .playback-duration {
margin-left: auto;
}
</style>