mirror of
https://github.com/janishutz/MusicPlayerV2.git
synced 2025-11-25 21:14:22 +00:00
Compare commits
3 Commits
d63df5898b
...
669cc620bf
| Author | SHA1 | Date | |
|---|---|---|---|
| 669cc620bf | |||
| 64d086dec4 | |||
| 1d714da494 |
@@ -431,11 +431,20 @@
|
|||||||
parseBlob( blob )
|
parseBlob( blob )
|
||||||
.then( data => {
|
.then( data => {
|
||||||
try {
|
try {
|
||||||
player.findSongOnAppleMusic( data.common.title
|
const searchTerm = data.common.title
|
||||||
?? songDetails.filename.split( '.' )[ 0 ] )
|
? data.common.title + ( data.common.artist ? ' ' + data.common.artist : '' )
|
||||||
|
: songDetails.filename.split( '.' )[ 0 ].replace( '_', ' ' );
|
||||||
|
|
||||||
|
|
||||||
|
player.findSongOnAppleMusic( searchTerm )
|
||||||
.then( d => {
|
.then( d => {
|
||||||
|
if ( d.data.results.songs ) {
|
||||||
let url = d.data.results.songs.data[ 0 ].attributes.artwork.url;
|
let url = d.data.results.songs.data[ 0 ].attributes.artwork.url;
|
||||||
|
|
||||||
|
console.debug(
|
||||||
|
'Result used for', searchTerm, 'is', d.data.results.songs.data[0]
|
||||||
|
);
|
||||||
|
|
||||||
url = url.replace( '{w}', String( d.data.results.songs.data[ 0 ].attributes.artwork.width ) );
|
url = url.replace( '{w}', String( d.data.results.songs.data[ 0 ].attributes.artwork.width ) );
|
||||||
url = url.replace( '{h}', String( d.data.results.songs.data[ 0 ].attributes.artwork.height ) );
|
url = url.replace( '{h}', String( d.data.results.songs.data[ 0 ].attributes.artwork.height ) );
|
||||||
const song: Song = {
|
const song: Song = {
|
||||||
@@ -448,6 +457,20 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
resolve( song );
|
resolve( song );
|
||||||
|
} else {
|
||||||
|
const song: Song = {
|
||||||
|
'artist': data.common.artist ?? 'Unknown artist',
|
||||||
|
'title': data.common.title ?? 'Unknown song title',
|
||||||
|
'duration': data.format.duration ?? 1000,
|
||||||
|
'id': songDetails.url,
|
||||||
|
'origin': 'disk',
|
||||||
|
'cover': ''
|
||||||
|
};
|
||||||
|
|
||||||
|
console.warn( 'No results found for', searchTerm );
|
||||||
|
|
||||||
|
resolve( song );
|
||||||
|
}
|
||||||
} )
|
} )
|
||||||
.catch( e => {
|
.catch( e => {
|
||||||
console.error( e );
|
console.error( e );
|
||||||
@@ -560,7 +583,7 @@
|
|||||||
prepNiceDurationTime( playingSong );
|
prepNiceDurationTime( playingSong );
|
||||||
notificationHandler.emit( 'playlist-index-update', currentlyPlayingSongIndex.value );
|
notificationHandler.emit( 'playlist-index-update', currentlyPlayingSongIndex.value );
|
||||||
notificationHandler.emit( 'playback-update', isPlaying.value );
|
notificationHandler.emit( 'playback-update', isPlaying.value );
|
||||||
notificationHandler.emit( 'playback-start-update', new Date().getTime() - pos.value * 1000 );
|
notificationHandler.emit( 'playback-start-update', new Date().getTime() - ( pos.value * 1000 ) );
|
||||||
hasStarted = true;
|
hasStarted = true;
|
||||||
}, 2000 );
|
}, 2000 );
|
||||||
}
|
}
|
||||||
@@ -574,7 +597,7 @@
|
|||||||
nicePlaybackPos.value = '0' + minuteCount + ':';
|
nicePlaybackPos.value = '0' + minuteCount + ':';
|
||||||
}
|
}
|
||||||
|
|
||||||
const secondCount = Math.floor( pos.value - minuteCount * 60 );
|
const secondCount = Math.floor( pos.value - ( minuteCount * 60 ) );
|
||||||
|
|
||||||
if ( ( '' + secondCount ).length === 1 ) {
|
if ( ( '' + secondCount ).length === 1 ) {
|
||||||
nicePlaybackPos.value += '0' + secondCount;
|
nicePlaybackPos.value += '0' + secondCount;
|
||||||
@@ -591,7 +614,7 @@
|
|||||||
niceDuration.value = '-0' + minuteCounts + ':';
|
niceDuration.value = '-0' + minuteCounts + ':';
|
||||||
}
|
}
|
||||||
|
|
||||||
const secondCounts = Math.floor( ( playingSong.duration - pos.value ) - minuteCounts * 60 );
|
const secondCounts = Math.floor( ( playingSong.duration - pos.value ) - ( minuteCounts * 60 ) );
|
||||||
|
|
||||||
if ( ( '' + secondCounts ).length === 1 ) {
|
if ( ( '' + secondCounts ).length === 1 ) {
|
||||||
niceDuration.value += '0' + secondCounts;
|
niceDuration.value += '0' + secondCounts;
|
||||||
|
|||||||
@@ -1,88 +1,153 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1>Queue</h1>
|
<h1>Queue</h1>
|
||||||
<input type="file" multiple accept="audio/*" id="more-songs" class="small-buttons">
|
<input
|
||||||
<button @click="addNewSongs()" class="small-buttons" title="Load selected files"><span class="material-symbols-outlined">upload</span></button>
|
id="more-songs"
|
||||||
<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>
|
type="file"
|
||||||
<button @click="clearPlaylist()" class="small-buttons" title="Clear the playlist"><span class="material-symbols-outlined">delete</span></button>
|
multiple
|
||||||
<button title="Transmit additional information" class="small-buttons" @click="sendAdditionalInfo()"><span class="material-symbols-outlined">send</span></button>
|
accept="audio/*"
|
||||||
<p v-if="!hasSelectedSongs">Please select at least one song to proceed</p>
|
class="small-buttons"
|
||||||
<div class="playlist-box" id="pl-box">
|
>
|
||||||
|
<button class="small-buttons" title="Load selected files" @click="addNewSongs()">
|
||||||
|
<span class="material-symbols-outlined">upload</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="$props.isLoggedIntoAppleMusic"
|
||||||
|
class="small-buttons"
|
||||||
|
title="Search Apple Music for the song"
|
||||||
|
@click="openSearch()"
|
||||||
|
>
|
||||||
|
<span class="material-symbols-outlined">search</span>
|
||||||
|
</button>
|
||||||
|
<button class="small-buttons" title="Clear the playlist" @click="clearPlaylist()">
|
||||||
|
<span class="material-symbols-outlined">delete</span>
|
||||||
|
</button>
|
||||||
|
<button title="Transmit additional information" class="small-buttons" @click="sendAdditionalInfo()">
|
||||||
|
<span class="material-symbols-outlined">send</span>
|
||||||
|
</button>
|
||||||
|
<p v-if="!hasSelectedSongs">
|
||||||
|
Please select at least one song to proceed
|
||||||
|
</p>
|
||||||
|
<div id="pl-box" class="playlist-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? Maybe using retrieved ID from Apple Music? -->
|
<!-- 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? Maybe using retrieved ID from Apple Music? -->
|
||||||
<!-- 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
|
||||||
|
v-for="song in computedPlaylist"
|
||||||
|
:key="song.id"
|
||||||
|
class="song"
|
||||||
: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' )
|
||||||
+ ( ( !isPlaying && ( song.id === ( $props.playlist ? $props.playlist [ $props.currentlyPlaying ?? 0 ].id : '' ) ) ) ? ' active-song' : '' )">
|
+ ( ( !isPlaying && ( song.id === ( $props.playlist ? $props.playlist [ $props.currentlyPlaying ?? 0 ].id : '' ) ) ) ? ' active-song' : '' )"
|
||||||
<img :src="song.cover" alt="Song cover" class="song-cover">
|
>
|
||||||
|
<img
|
||||||
|
v-if="song.cover"
|
||||||
|
:src="song.cover"
|
||||||
|
alt="Song cover"
|
||||||
|
class="song-cover"
|
||||||
|
>
|
||||||
|
<span v-else class="material-symbols-outlined song-cover">music_note</span>
|
||||||
<div v-if="song.id === ( $props.playlist ? $props.playlist [ $props.currentlyPlaying ?? 0 ].id : '' ) && $props.isPlaying" class="playing-symbols">
|
<div v-if="song.id === ( $props.playlist ? $props.playlist [ $props.currentlyPlaying ?? 0 ].id : '' ) && $props.isPlaying" class="playing-symbols">
|
||||||
<div class="playing-symbols-wrapper">
|
<div class="playing-symbols-wrapper">
|
||||||
<div class="playing-bar" id="bar-1"></div>
|
<div id="bar-1" class="playing-bar"></div>
|
||||||
<div class="playing-bar" id="bar-2"></div>
|
<div id="bar-2" class="playing-bar"></div>
|
||||||
<div class="playing-bar" id="bar-3"></div>
|
<div id="bar-3" class="playing-bar"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="material-symbols-outlined play-icon" @click="control( 'play' )" v-if="song.id === ( $props.playlist ? $props.playlist [ $props.currentlyPlaying ?? 0 ].id : '' )">play_arrow</span>
|
<span v-if="song.id === ( $props.playlist ? $props.playlist [ $props.currentlyPlaying ?? 0 ].id : '' )" class="material-symbols-outlined play-icon" @click="control( 'play' )">play_arrow</span>
|
||||||
<span class="material-symbols-outlined play-icon" @click="play( song.id )" v-else>play_arrow</span>
|
<span v-else class="material-symbols-outlined play-icon" @click="play( song.id )">play_arrow</span>
|
||||||
<span class="material-symbols-outlined pause-icon" @click="control( 'pause' )">pause</span>
|
<span class="material-symbols-outlined pause-icon" @click="control( 'pause' )">pause</span>
|
||||||
<span class="material-symbols-outlined move-icon" @click="moveSong( song.id, 'up' )" title="Move song up" v-if="canBeMoved( 'up', song.id )">arrow_upward</span>
|
<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>
|
v-if="canBeMoved( 'up', song.id )"
|
||||||
<h3 class="song-title">{{ song.title }}</h3>
|
class="material-symbols-outlined move-icon"
|
||||||
|
title="Move song up"
|
||||||
|
@click="moveSong( song.id, 'up' )"
|
||||||
|
>arrow_upward</span>
|
||||||
|
<span
|
||||||
|
v-if="canBeMoved( 'down', song.id )"
|
||||||
|
class="material-symbols-outlined move-icon"
|
||||||
|
title="Move song down"
|
||||||
|
@click="moveSong( song.id, 'down' )"
|
||||||
|
>arrow_downward</span>
|
||||||
|
<h3 class="song-title">
|
||||||
|
{{ song.title }}
|
||||||
|
</h3>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" placeholder="Additional information for remote display" title="Additional information for remote display" v-model="song.additionalInfo" @focusin="kbControl( 'on' )" @focusout="kbControl( 'off' )">
|
<input
|
||||||
<p class="playing-in">{{ getTimeUntil( song ) }}</p>
|
v-model="song.additionalInfo"
|
||||||
|
type="text"
|
||||||
|
placeholder="Additional information for remote display"
|
||||||
|
title="Additional information for remote display"
|
||||||
|
@focusin="kbControl( 'on' )"
|
||||||
|
@focusout="kbControl( 'off' )"
|
||||||
|
>
|
||||||
|
<p class="playing-in">
|
||||||
|
{{ getTimeUntil( song ) }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<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>
|
<button
|
||||||
|
v-if="canBeMoved( 'down', song.id ) || canBeMoved( 'up', song.id )"
|
||||||
|
class="small-buttons"
|
||||||
|
title="Remove this song from the queue"
|
||||||
|
@click="deleteSong( 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 ) }" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// TODO: Add logout button
|
// TODO: Add logout button
|
||||||
import type { AppleMusicSongData, ReadFile, Song } from '@/scripts/song';
|
import type {
|
||||||
import { computed, ref } from 'vue';
|
AppleMusicSongData, ReadFile, Song
|
||||||
|
} from '@/scripts/song';
|
||||||
|
import {
|
||||||
|
computed, ref
|
||||||
|
} from 'vue';
|
||||||
import searchView from './searchView.vue';
|
import searchView from './searchView.vue';
|
||||||
import { useUserStore } from '@/stores/userStore';
|
import {
|
||||||
|
useUserStore
|
||||||
|
} from '@/stores/userStore';
|
||||||
|
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
|
|
||||||
const search = ref( searchView );
|
const search = ref( searchView );
|
||||||
const props = defineProps( {
|
const props = defineProps( {
|
||||||
'playlist': {
|
'playlist': {
|
||||||
default: [],
|
'default': [],
|
||||||
required: true,
|
'required': true,
|
||||||
type: Array<Song>
|
'type': Array<Song>
|
||||||
},
|
},
|
||||||
'currentlyPlaying': {
|
'currentlyPlaying': {
|
||||||
default: 0,
|
'default': 0,
|
||||||
required: true,
|
'required': true,
|
||||||
type: Number,
|
'type': Number,
|
||||||
},
|
},
|
||||||
'isPlaying': {
|
'isPlaying': {
|
||||||
default: true,
|
'default': true,
|
||||||
required: true,
|
'required': true,
|
||||||
type: Boolean,
|
'type': Boolean,
|
||||||
},
|
},
|
||||||
'pos': {
|
'pos': {
|
||||||
default: 0,
|
'default': 0,
|
||||||
required: false,
|
'required': false,
|
||||||
type: Number,
|
'type': Number,
|
||||||
},
|
},
|
||||||
'isLoggedIntoAppleMusic': {
|
'isLoggedIntoAppleMusic': {
|
||||||
default: false,
|
'default': false,
|
||||||
required: true,
|
'required': true,
|
||||||
type: Boolean,
|
'type': Boolean,
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
const hasSelectedSongs = ref( true );
|
const hasSelectedSongs = ref( true );
|
||||||
|
|
||||||
const computedPlaylist = computed( () => {
|
const computedPlaylist = computed( () => {
|
||||||
let pl: Song[] = [];
|
let pl: Song[] = [];
|
||||||
|
|
||||||
// ( document.getElementById( 'pl-box' ) as HTMLDivElement ).scrollTo( { behavior: 'smooth', top: 0 } );
|
// ( document.getElementById( 'pl-box' ) as HTMLDivElement ).scrollTo( { behavior: 'smooth', top: 0 } );
|
||||||
for ( let i = props.currentlyPlaying; i < props.playlist.length; i++ ) {
|
for ( let i = props.currentlyPlaying; i < props.playlist.length; i++ ) {
|
||||||
pl.push( props.playlist[ i ] );
|
pl.push( props.playlist[ i ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
return pl;
|
return pl;
|
||||||
} );
|
} );
|
||||||
|
|
||||||
@@ -92,46 +157,52 @@
|
|||||||
} else {
|
} else {
|
||||||
userStore.setKeyboardUsageStatus( true );
|
userStore.setKeyboardUsageStatus( true );
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const openSearch = () => {
|
const openSearch = () => {
|
||||||
if ( search.value ) {
|
if ( search.value ) {
|
||||||
search.value.controlSearch( 'show' );
|
search.value.controlSearch( 'show' );
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const canBeMoved = computed( () => {
|
const canBeMoved = computed( () => {
|
||||||
return ( direction: movementDirection, songID: string ): boolean => {
|
return ( direction: movementDirection, songID: string ): boolean => {
|
||||||
let id = 0;
|
let id = 0;
|
||||||
|
|
||||||
for ( let song in props.playlist ) {
|
for ( let song in props.playlist ) {
|
||||||
if ( props.playlist[ song ].id === songID ) {
|
if ( props.playlist[ song ].id === songID ) {
|
||||||
id = parseInt( song );
|
id = parseInt( song );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( direction === 'up' ) {
|
if ( direction === 'up' ) {
|
||||||
if ( props.currentlyPlaying + 1 === id || props.currentlyPlaying === id ) {
|
if ( props.currentlyPlaying + 1 === id || props.currentlyPlaying === id ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if ( id === props.playlist.length - 1 || props.currentlyPlaying === id ) {
|
if ( id === props.playlist.length - 1 || props.currentlyPlaying === id ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
} )
|
} );
|
||||||
|
|
||||||
const getTimeUntil = computed( () => {
|
const getTimeUntil = computed( () => {
|
||||||
return ( song: Song ) => {
|
return ( song: Song ) => {
|
||||||
let timeRemaining = 0;
|
let timeRemaining = 0;
|
||||||
|
|
||||||
for ( let i = props.currentlyPlaying; i < Object.keys( props.playlist ).length; i++ ) {
|
for ( let i = props.currentlyPlaying; i < Object.keys( props.playlist ).length; i++ ) {
|
||||||
if ( props.playlist[ i ] == song ) {
|
if ( props.playlist[ i ] == song ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
timeRemaining += props.playlist[ i ].duration;
|
timeRemaining += props.playlist[ i ].duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( props.isPlaying ) {
|
if ( props.isPlaying ) {
|
||||||
if ( timeRemaining === 0 ) {
|
if ( timeRemaining === 0 ) {
|
||||||
return 'Currently playing';
|
return 'Currently playing';
|
||||||
@@ -145,7 +216,7 @@
|
|||||||
return 'Playing less than ' + Math.ceil( timeRemaining / 60 - props.pos / 60 ) + 'min after starting to play';
|
return 'Playing less than ' + Math.ceil( timeRemaining / 60 - props.pos / 60 ) + 'min after starting to play';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
} );
|
} );
|
||||||
|
|
||||||
const deleteSong = ( songID: string ) => {
|
const deleteSong = ( songID: string ) => {
|
||||||
@@ -154,69 +225,92 @@
|
|||||||
emits( 'delete-song', parseInt( song ) );
|
emits( 'delete-song', parseInt( song ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const clearPlaylist = () => {
|
const clearPlaylist = () => {
|
||||||
emits( 'clear-playlist', '' );
|
emits( 'clear-playlist', '' );
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
const control = ( action: string ) => {
|
const control = ( action: string ) => {
|
||||||
emits( 'control', action );
|
emits( 'control', action );
|
||||||
}
|
};
|
||||||
|
|
||||||
const play = ( song: string ) => {
|
const play = ( song: string ) => {
|
||||||
emits( 'play-song', song );
|
emits( 'play-song', song );
|
||||||
}
|
};
|
||||||
|
|
||||||
const addNewSongs = () => {
|
const addNewSongs = () => {
|
||||||
const fileURLList: ReadFile[] = [];
|
const fileURLList: ReadFile[] = [];
|
||||||
const allFiles = ( document.getElementById( 'more-songs' ) as HTMLInputElement ).files ?? [];
|
const allFiles = ( document.getElementById( 'more-songs' ) as HTMLInputElement ).files ?? [];
|
||||||
|
|
||||||
if ( allFiles.length > 0 ) {
|
if ( allFiles.length > 0 ) {
|
||||||
hasSelectedSongs.value = true;
|
hasSelectedSongs.value = true;
|
||||||
|
|
||||||
for ( let file = 0; file < allFiles.length; file++ ) {
|
for ( let file = 0; file < allFiles.length; file++ ) {
|
||||||
fileURLList.push( { 'url': URL.createObjectURL( allFiles[ file ] ), 'filename': allFiles[ file ].name } );
|
fileURLList.push( {
|
||||||
|
'url': URL.createObjectURL( allFiles[ file ] ),
|
||||||
|
'filename': allFiles[ file ].name
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
emits( 'add-new-songs', fileURLList );
|
emits( 'add-new-songs', fileURLList );
|
||||||
} else {
|
} else {
|
||||||
hasSelectedSongs.value = false;
|
hasSelectedSongs.value = false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const addNewSongsAppleMusic = ( songData: AppleMusicSongData ) => {
|
const addNewSongsAppleMusic = ( songData: AppleMusicSongData ) => {
|
||||||
const song: Song = {
|
const song: Song = {
|
||||||
artist: songData.attributes.artistName,
|
'artist': songData.attributes.artistName,
|
||||||
cover: songData.attributes.artwork.url.replace( '{w}', String( songData.attributes.artwork.width ) ).replace( '{h}', String( songData.attributes.artwork.height ) ),
|
'cover': songData.attributes.artwork.url.replace( '{w}', String( songData.attributes.artwork.width ) ).replace( '{h}', String( songData.attributes.artwork.height ) ),
|
||||||
duration: songData.attributes.durationInMillis / 1000,
|
'duration': songData.attributes.durationInMillis / 1000,
|
||||||
id: songData.id,
|
'id': songData.id,
|
||||||
origin: 'apple-music',
|
'origin': 'apple-music',
|
||||||
title: songData.attributes.name
|
'title': songData.attributes.name
|
||||||
}
|
};
|
||||||
|
|
||||||
emits( 'add-new-songs-apple-music', song );
|
emits( 'add-new-songs-apple-music', song );
|
||||||
}
|
};
|
||||||
|
|
||||||
type movementDirection = 'up' | 'down';
|
type movementDirection = 'up' | 'down';
|
||||||
|
|
||||||
const moveSong = ( songID: string, direction: movementDirection ) => {
|
const moveSong = ( songID: string, direction: movementDirection ) => {
|
||||||
let newSongPos = 0;
|
let newSongPos = 0;
|
||||||
let hasFoundSongToMove = false;
|
let hasFoundSongToMove = false;
|
||||||
|
|
||||||
for ( let el in props.playlist ) {
|
for ( let el in props.playlist ) {
|
||||||
if ( props.playlist[ el ].id === songID ) {
|
if ( props.playlist[ el ].id === songID ) {
|
||||||
const currPos = parseInt( el );
|
const currPos = parseInt( el );
|
||||||
|
|
||||||
newSongPos = currPos + ( direction === 'up' ? -1 : 1 );
|
newSongPos = currPos + ( direction === 'up' ? -1 : 1 );
|
||||||
hasFoundSongToMove = true;
|
hasFoundSongToMove = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( hasFoundSongToMove ) {
|
if ( hasFoundSongToMove ) {
|
||||||
emits( 'playlist-reorder', { 'songID': songID, 'newPos': newSongPos } );
|
emits( 'playlist-reorder', {
|
||||||
}
|
'songID': songID,
|
||||||
|
'newPos': newSongPos
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const sendAdditionalInfo = () => {
|
const sendAdditionalInfo = () => {
|
||||||
emits( 'send-additional-info' );
|
emits( 'send-additional-info' );
|
||||||
}
|
};
|
||||||
|
|
||||||
const emits = defineEmits( [ 'play-song', 'control', 'playlist-reorder', 'add-new-songs', 'add-new-songs-apple-music', 'delete-song', 'clear-playlist', 'send-additional-info' ] );
|
const emits = defineEmits( [
|
||||||
|
'play-song',
|
||||||
|
'control',
|
||||||
|
'playlist-reorder',
|
||||||
|
'add-new-songs',
|
||||||
|
'add-new-songs-apple-music',
|
||||||
|
'delete-song',
|
||||||
|
'clear-playlist',
|
||||||
|
'send-additional-info'
|
||||||
|
] );
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -249,6 +343,10 @@
|
|||||||
font-size: 6rem;
|
font-size: 6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.song img.song-cover {
|
||||||
|
font-size: unset;
|
||||||
|
}
|
||||||
|
|
||||||
.song-title {
|
.song-title {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<div v-if="hasLoaded && !showCouldNotFindRoom" style="width: 100%">
|
<div v-if="hasLoaded && !showCouldNotFindRoom" style="width: 100%">
|
||||||
<div class="current-song-wrapper">
|
<div class="current-song-wrapper">
|
||||||
<img
|
<img
|
||||||
v-if="playlist[ playingSong ]"
|
v-if="playlist[ playingSong ] && playlist[ playingSong ].cover"
|
||||||
id="current-image"
|
id="current-image"
|
||||||
:src="playlist[ playingSong ].cover"
|
:src="playlist[ playingSong ].cover"
|
||||||
class="fancy-view-song-art"
|
class="fancy-view-song-art"
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<div v-if="hasLoaded && !showCouldNotFindRoom" class="showcase-wrapper">
|
<div v-if="hasLoaded && !showCouldNotFindRoom" class="showcase-wrapper">
|
||||||
<div class="current-song-wrapper">
|
<div class="current-song-wrapper">
|
||||||
<img
|
<img
|
||||||
v-if="playlist[ playingSong ]"
|
v-if="playlist[ playingSong ] && playlist[ playingSong ].cover"
|
||||||
id="current-image"
|
id="current-image"
|
||||||
:src="playlist[ playingSong ].cover"
|
:src="playlist[ playingSong ].cover"
|
||||||
class="fancy-view-song-art"
|
class="fancy-view-song-art"
|
||||||
@@ -52,7 +52,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="song-list-wrapper">
|
<div class="song-list-wrapper">
|
||||||
<div v-for="song in songQueue" :key="song.id" class="song-list">
|
<div v-for="song in songQueue" :key="song.id" class="song-list">
|
||||||
<img :src="song.cover" class="song-image">
|
<img v-if="song.cover" :src="song.cover" class="song-image">
|
||||||
|
<span v-else class="material-symbols-outlined song-cover">music_note</span>
|
||||||
<div
|
<div
|
||||||
v-if="( playlist[ playingSong ] ? playlist[ playingSong ].id : '' ) === song.id && isPlaying"
|
v-if="( playlist[ playingSong ] ? playlist[ playingSong ].id : '' ) === song.id && isPlaying"
|
||||||
class="playing-symbols"
|
class="playing-symbols"
|
||||||
@@ -97,7 +98,7 @@
|
|||||||
Song
|
Song
|
||||||
} from '@/scripts/song';
|
} from '@/scripts/song';
|
||||||
import {
|
import {
|
||||||
computed, ref, type Ref
|
type Ref, computed, ref
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import bizualizer from '@/scripts/bizualizer';
|
import bizualizer from '@/scripts/bizualizer';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user