mirror of
https://github.com/janishutz/MusicPlayerV2.git
synced 2025-11-25 04:54:23 +00:00
Design changes
This commit is contained in:
@@ -62,7 +62,9 @@
|
|||||||
@control="( action ) => { control( action ) }" @play-song="( song ) => { playSong( song ) }"
|
@control="( action ) => { control( action ) }" @play-song="( song ) => { playSong( song ) }"
|
||||||
@add-new-songs="( songs ) => addNewSongs( songs )" @playlist-reorder="( move ) => moveSong( move )"
|
@add-new-songs="( songs ) => addNewSongs( songs )" @playlist-reorder="( move ) => moveSong( move )"
|
||||||
:is-logged-into-apple-music="player.isLoggedIn"
|
:is-logged-into-apple-music="player.isLoggedIn"
|
||||||
@add-new-songs-apple-music="( song ) => addNewSongFromObject( song )"></playlistView>
|
@add-new-songs-apple-music="( song ) => addNewSongFromObject( song )"
|
||||||
|
@delete-song="song => removeSongFromPlaylist( song )"
|
||||||
|
@clear-playlist="() => clearPlaylist()"></playlistView>
|
||||||
</div>
|
</div>
|
||||||
<notificationsModule ref="notifications" location="bottomleft" size="bigger"></notificationsModule>
|
<notificationsModule ref="notifications" location="bottomleft" size="bigger"></notificationsModule>
|
||||||
<popupModule @update="( data ) => popupReturnHandler( data )" ref="popup"></popupModule>
|
<popupModule @update="( data ) => popupReturnHandler( data )" ref="popup"></popupModule>
|
||||||
@@ -489,6 +491,31 @@
|
|||||||
notificationHandler.emit( 'playlist-update', playlist.value );
|
notificationHandler.emit( 'playlist-update', playlist.value );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const removeSongFromPlaylist = ( song: number ) => {
|
||||||
|
playlist.value = player.getQueue();
|
||||||
|
playlist.value.splice( song, 1 );
|
||||||
|
player.setPlaylist( playlist.value );
|
||||||
|
if ( !isPlaying.value ) {
|
||||||
|
player.prepare( 0 );
|
||||||
|
isPlaying.value = true;
|
||||||
|
startProgressTracker();
|
||||||
|
}
|
||||||
|
notificationHandler.emit( 'playlist-update', playlist.value );
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearPlaylist = () => {
|
||||||
|
playlist.value = [];
|
||||||
|
player.control( 'pause' );
|
||||||
|
stopProgressTracker();
|
||||||
|
isPlaying.value = false;
|
||||||
|
player.setPlaylist( [] );
|
||||||
|
currentlyPlayingSongArtist.value = '';
|
||||||
|
currentlyPlayingSongName.value = 'Not playing';
|
||||||
|
coverArt.value = '';
|
||||||
|
pos.value = 0;
|
||||||
|
notificationHandler.emit( 'playlist-update', playlist.value );
|
||||||
|
}
|
||||||
|
|
||||||
emits( 'playerStateChange', isShowingFullScreenPlayer.value ? 'show' : 'hide' );
|
emits( 'playerStateChange', isShowingFullScreenPlayer.value ? 'show' : 'hide' );
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1>Playlist</h1>
|
<h1>Queue</h1>
|
||||||
<input type="file" multiple accept="audio/*" id="more-songs" class="small-buttons">
|
<input type="file" multiple accept="audio/*" id="more-songs" class="small-buttons">
|
||||||
<button @click="addNewSongs()" class="small-buttons" title="Load selected files"><span class="material-symbols-outlined">upload</span></button>
|
<button @click="addNewSongs()" class="small-buttons" title="Load selected files"><span class="material-symbols-outlined">upload</span></button>
|
||||||
<button @click="openSearch()" v-if="$props.isLoggedIntoAppleMusic" class="small-buttons" title="Search Apple Music for the song"><span class="material-symbols-outlined">search</span></button>
|
<button @click="openSearch()" v-if="$props.isLoggedIntoAppleMusic" class="small-buttons" title="Search Apple Music for the song"><span class="material-symbols-outlined">search</span></button>
|
||||||
|
<button @click="clearPlaylist()" class="small-buttons" title="Clear the playlist"><span class="material-symbols-outlined">delete</span></button>
|
||||||
<p v-if="!hasSelectedSongs">Please select at least one song to proceed</p>
|
<p v-if="!hasSelectedSongs">Please select at least one song to proceed</p>
|
||||||
<div class="playlist-box" id="pl-box">
|
<div class="playlist-box" id="pl-box">
|
||||||
<!-- TODO: Allow editing additionalInfo. Think also how to make it persist over reloads... Export to JSON and then best-guess add them? Very easy for Apple Music 'cause ID, but how for local songs? -->
|
<!-- TODO: Allow editing additionalInfo. Think also how to make it persist over reloads... Export to JSON and then best-guess add them? Very easy for Apple Music 'cause ID, but how for local songs? -->
|
||||||
<!-- TODO: Allow deleting songs, as well as whole playlist -->
|
<!-- TODO: Allow deleting songs, as well as whole playlist -> Handle on player side -->
|
||||||
<!-- TODO: Handle long AppleMusic Playlists, as AppleMusic doesn't automatically load all songs of a playlist -->
|
<!-- TODO: Handle long AppleMusic Playlists, as AppleMusic doesn't automatically load all songs of a playlist -->
|
||||||
<div class="song" v-for="song in computedPlaylist" v-bind:key="song.id"
|
<div class="song" v-for="song in computedPlaylist" v-bind:key="song.id"
|
||||||
:class="( song.id === ( $props.playlist ? $props.playlist [ $props.currentlyPlaying ?? 0 ].id : '' ) && isPlaying ? 'playing' : ' not-playing' )
|
:class="( song.id === ( $props.playlist ? $props.playlist [ $props.currentlyPlaying ?? 0 ].id : '' ) && isPlaying ? 'playing' : ' not-playing' )
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
<span class="material-symbols-outlined move-icon" @click="moveSong( song.id, 'down' )" title="Move song down" v-if="canBeMoved( 'down', song.id )">arrow_downward</span>
|
<span class="material-symbols-outlined move-icon" @click="moveSong( song.id, 'down' )" title="Move song down" v-if="canBeMoved( 'down', song.id )">arrow_downward</span>
|
||||||
<h3 class="song-title">{{ song.title }}</h3>
|
<h3 class="song-title">{{ song.title }}</h3>
|
||||||
<p class="playing-in">{{ getTimeUntil( song ) }}</p>
|
<p class="playing-in">{{ getTimeUntil( song ) }}</p>
|
||||||
|
<button @click="deleteSong( song.id )" class="small-buttons" title="Remove this song from the queue" v-if="canBeMoved( 'down', song.id ) || canBeMoved( 'up', song.id )"><span class="material-symbols-outlined">delete</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<searchView ref="search" @selected-song="( song ) => { addNewSongsAppleMusic( song ) }"></searchView>
|
<searchView ref="search" @selected-song="( song ) => { addNewSongsAppleMusic( song ) }"></searchView>
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
// TODO: Add logout button
|
||||||
import type { AppleMusicSongData, ReadFile, Song } from '@/scripts/song';
|
import type { AppleMusicSongData, ReadFile, Song } from '@/scripts/song';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import searchView from './searchView.vue';
|
import searchView from './searchView.vue';
|
||||||
@@ -131,6 +134,18 @@
|
|||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
const deleteSong = ( songID: string ) => {
|
||||||
|
for ( const song in props.playlist ) {
|
||||||
|
if ( props.playlist[ song ].id === songID ) {
|
||||||
|
emits( 'delete-song', parseInt( song ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearPlaylist = () => {
|
||||||
|
emits( 'clear-playlist', '' );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const control = ( action: string ) => {
|
const control = ( action: string ) => {
|
||||||
emits( 'control', action );
|
emits( 'control', action );
|
||||||
@@ -183,7 +198,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const emits = defineEmits( [ 'play-song', 'control', 'playlist-reorder', 'add-new-songs', 'add-new-songs-apple-music' ] );
|
const emits = defineEmits( [ 'play-song', 'control', 'playlist-reorder', 'add-new-songs', 'add-new-songs-apple-music', 'delete-song', 'clear-playlist' ] );
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
<img v-if="playlist[ playingSong ]" :src="playlist[ playingSong ].cover" class="fancy-view-song-art" id="current-image" crossorigin="anonymous">
|
<img v-if="playlist[ playingSong ]" :src="playlist[ playingSong ].cover" class="fancy-view-song-art" id="current-image" crossorigin="anonymous">
|
||||||
<span v-else class="material-symbols-outlined fancy-view-song-art">music_note</span>
|
<span v-else class="material-symbols-outlined fancy-view-song-art">music_note</span>
|
||||||
<div class="current-song">
|
<div class="current-song">
|
||||||
<progress max="1000" id="progress" :value="progressBar"></progress>
|
<h1 style="margin-bottom: 5px;">{{ playlist[ playingSong ] ? playlist[ playingSong ].title : 'Not playing' }}</h1>
|
||||||
<h1>{{ playlist[ playingSong ] ? playlist[ playingSong ].title : 'Not playing' }}</h1>
|
|
||||||
<p class="additional-info" v-if="playlist[ playingSong ] ? ( playlist[ playingSong ].additionalInfo !== '' ) : false">{{ playlist[ playingSong ] ? playlist[ playingSong ].additionalInfo : '' }}</p>
|
|
||||||
<p>{{ playlist[ playingSong ] ? playlist[ playingSong ].artist : '' }}</p>
|
<p>{{ playlist[ playingSong ] ? playlist[ playingSong ].artist : '' }}</p>
|
||||||
|
<p class="additional-info" v-if="playlist[ playingSong ] ? ( playlist[ playingSong ].additionalInfo !== '' ) : false">{{ playlist[ playingSong ] ? playlist[ playingSong ].additionalInfo : '' }}</p>
|
||||||
|
<progress max="1000" id="progress" :value="progressBar"></progress>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="song-list-wrapper">
|
<div class="song-list-wrapper">
|
||||||
@@ -156,6 +156,12 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#themeSelector {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.remote-view {
|
.remote-view {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -164,6 +170,8 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
|
background-color: rgb(2, 16, 61);
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loaded {
|
.loaded {
|
||||||
@@ -214,6 +222,7 @@
|
|||||||
padding: 1vh;
|
padding: 1vh;
|
||||||
border: 1px white solid;
|
border: 1px white solid;
|
||||||
background-color: rgba( 0, 0, 0, 0.4 );
|
background-color: rgba( 0, 0, 0, 0.4 );
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.song-details-wrapper {
|
.song-details-wrapper {
|
||||||
@@ -225,14 +234,6 @@
|
|||||||
text-align: justify;
|
text-align: justify;
|
||||||
}
|
}
|
||||||
|
|
||||||
.song-list .song-image {
|
|
||||||
width: 5vw;
|
|
||||||
height: 5vw;
|
|
||||||
object-fit: cover;
|
|
||||||
object-position: center;
|
|
||||||
font-size: 5vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pause-icon {
|
.pause-icon {
|
||||||
width: 5vw;
|
width: 5vw;
|
||||||
height: 5vw;
|
height: 5vw;
|
||||||
@@ -260,6 +261,7 @@
|
|||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: rgba( 0, 0, 0, 0.4 );
|
background-color: rgba( 0, 0, 0, 0.4 );
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fancy-view-song-art {
|
.fancy-view-song-art {
|
||||||
@@ -269,6 +271,7 @@
|
|||||||
object-position: center;
|
object-position: center;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
font-size: 30vh !important;
|
font-size: 30vh !important;
|
||||||
|
border-radius: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
@@ -276,14 +279,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#progress, #progress::-webkit-progress-bar {
|
#progress, #progress::-webkit-progress-bar {
|
||||||
background-color: rgba(45, 28, 145);
|
background-color: rgb(82, 82, 82);
|
||||||
color: rgba(45, 28, 145);
|
color: rgb(82, 82, 82);
|
||||||
width: 30vw;
|
width: 30vw;
|
||||||
|
height: 10px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
accent-color: white;
|
accent-color: white;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#progress::-moz-progress-bar {
|
#progress::-moz-progress-bar {
|
||||||
@@ -320,6 +326,7 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-until {
|
.time-until {
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
<img v-if="playlist[ playingSong ]" :src="playlist[ playingSong ].cover" class="fancy-view-song-art" id="current-image" crossorigin="anonymous">
|
<img v-if="playlist[ playingSong ]" :src="playlist[ playingSong ].cover" class="fancy-view-song-art" id="current-image" crossorigin="anonymous">
|
||||||
<span v-else class="material-symbols-outlined fancy-view-song-art">music_note</span>
|
<span v-else class="material-symbols-outlined fancy-view-song-art">music_note</span>
|
||||||
<div class="current-song">
|
<div class="current-song">
|
||||||
<progress max="1000" id="progress" :value="progressBar"></progress>
|
<h1 style="margin-bottom: 5px;">{{ playlist[ playingSong ] ? playlist[ playingSong ].title : 'Not playing' }}</h1>
|
||||||
<h1>{{ playlist[ playingSong ] ? playlist[ playingSong ].title : 'Not playing' }}</h1>
|
|
||||||
<p class="additional-info" v-if="playlist[ playingSong ] ? ( playlist[ playingSong ].additionalInfo !== '' ) : false">{{ playlist[ playingSong ] ? playlist[ playingSong ].additionalInfo : '' }}</p>
|
|
||||||
<p>{{ playlist[ playingSong ] ? playlist[ playingSong ].artist : '' }}</p>
|
<p>{{ playlist[ playingSong ] ? playlist[ playingSong ].artist : '' }}</p>
|
||||||
|
<p class="additional-info" v-if="playlist[ playingSong ] ? ( playlist[ playingSong ].additionalInfo !== '' ) : false">{{ playlist[ playingSong ] ? playlist[ playingSong ].additionalInfo : '' }}</p>
|
||||||
|
<progress max="1000" id="progress" :value="progressBar"></progress>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mode-selector-wrapper">
|
<div class="mode-selector-wrapper">
|
||||||
@@ -282,6 +282,7 @@
|
|||||||
width: 5vw;
|
width: 5vw;
|
||||||
height: 5vw;
|
height: 5vw;
|
||||||
background-color: rgba( 0, 0, 0, 0.6 );
|
background-color: rgba( 0, 0, 0, 0.6 );
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.playing-symbols-wrapper {
|
.playing-symbols-wrapper {
|
||||||
@@ -328,6 +329,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.song-list-wrapper {
|
.song-list-wrapper {
|
||||||
|
border-radius: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -345,6 +347,7 @@
|
|||||||
padding: 1vh;
|
padding: 1vh;
|
||||||
border: 1px white solid;
|
border: 1px white solid;
|
||||||
background-color: rgba( 0, 0, 0, 0.4 );
|
background-color: rgba( 0, 0, 0, 0.4 );
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.song-details-wrapper {
|
.song-details-wrapper {
|
||||||
@@ -361,6 +364,7 @@
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
object-position: center;
|
object-position: center;
|
||||||
font-size: 5vw;
|
font-size: 5vw;
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pause-icon {
|
.pause-icon {
|
||||||
@@ -392,6 +396,7 @@
|
|||||||
padding: 1vh;
|
padding: 1vh;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: rgba( 0, 0, 0, 0.4 );
|
background-color: rgba( 0, 0, 0, 0.4 );
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fancy-view-song-art {
|
.fancy-view-song-art {
|
||||||
@@ -401,6 +406,7 @@
|
|||||||
object-position: center;
|
object-position: center;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
font-size: 30vh !important;
|
font-size: 30vh !important;
|
||||||
|
border-radius: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
@@ -408,14 +414,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#progress, #progress::-webkit-progress-bar {
|
#progress, #progress::-webkit-progress-bar {
|
||||||
background-color: rgba(45, 28, 145);
|
background-color: rgb(82, 82, 82);
|
||||||
color: rgba(45, 28, 145);
|
color: rgb(82, 82, 82);
|
||||||
width: 30vw;
|
width: 30vw;
|
||||||
|
height: 10px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
accent-color: white;
|
accent-color: white;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#progress::-moz-progress-bar {
|
#progress::-moz-progress-bar {
|
||||||
|
|||||||
Reference in New Issue
Block a user