mirror of
https://github.com/janishutz/MusicPlayer.git
synced 2026-09-10 14:25:24 +02:00
chore: refactor vue components
This commit is contained in:
@@ -0,0 +1,49 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
queue,
|
||||||
|
queueIdx
|
||||||
|
} from '@/ts/player/state';
|
||||||
|
import CurrentSong from '../player/CurrentSong.vue';
|
||||||
|
import PlayerControls from '../player/PlayerControls.vue';
|
||||||
|
import QueueViewer from '../player/QueueViewer.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="player">
|
||||||
|
<div
|
||||||
|
class="panel"
|
||||||
|
>
|
||||||
|
<div style="margin-bottom: 20px; width: 100%;">
|
||||||
|
<CurrentSong
|
||||||
|
v-model="queue[queueIdx]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<PlayerControls />
|
||||||
|
</div>
|
||||||
|
<div class="panel">
|
||||||
|
<QueueViewer />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.player {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
width: 45%;
|
||||||
|
margin-left: 2.5%;
|
||||||
|
margin-right: 2.5%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import PlayerComponent from './PlayerComponent.vue';
|
||||||
|
import SmallPlayerComponent from './SmallPlayerComponent.vue';
|
||||||
|
import {
|
||||||
|
ref
|
||||||
|
} from 'vue';
|
||||||
|
|
||||||
|
const fullPlayer = ref( true );
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
fullPlayer.value = false;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="player-wrapper">
|
||||||
|
<SmallPlayerComponent v-model="fullPlayer" />
|
||||||
|
<div :class="['player-container', fullPlayer ? undefined : 'hidden']">
|
||||||
|
<i class="fa-solid fa-xmark" @click="close"></i>
|
||||||
|
<PlayerComponent />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.player-container {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: var(--secondary-color);
|
||||||
|
transition: bottom 1s ease;
|
||||||
|
transition-delay: 0.25s;
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
transition-delay: 0s;
|
||||||
|
bottom: -100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.fa-xmark {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
Hello World
|
<h1>Playlists</h1>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const closed = defineModel<boolean>( {
|
||||||
|
'required': true
|
||||||
|
} );
|
||||||
|
|
||||||
|
const open = () => {
|
||||||
|
closed.value = true;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="['small-player', closed ? 'hidden' : undefined]" @click="open">
|
||||||
|
Player
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.small-player {
|
||||||
|
background-color: green;
|
||||||
|
width: 80vw;
|
||||||
|
height: 7vh;
|
||||||
|
position: fixed;
|
||||||
|
left: 10vw;
|
||||||
|
bottom: 15px;
|
||||||
|
border-radius: 4vh;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: bottom 0.5s ease;
|
||||||
|
transition-delay: 0.75s;
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
bottom: -8vh;
|
||||||
|
transition-delay: 0s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -5,12 +5,12 @@
|
|||||||
computed,
|
computed,
|
||||||
ref
|
ref
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import AddSong from './AddSong.vue';
|
import AddSong from '../popups/AddSong.vue';
|
||||||
import type {
|
import type {
|
||||||
Song
|
Song
|
||||||
} from '@/ts/dtype/playlist';
|
} from '@/ts/dtype/playlist';
|
||||||
import SongEditor from './SongEditor.vue';
|
import SongEditor from '../popups/SongEditor.vue';
|
||||||
import SortableList from './SortableList.vue';
|
import SortableList from '../SortableList.vue';
|
||||||
import {
|
import {
|
||||||
beautifyTime
|
beautifyTime
|
||||||
} from '@/ts/util/time';
|
} from '@/ts/util/time';
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import DiskLoader from '@/composables/DiskLoader.vue';
|
import DiskLoader from '@/composables/DiskLoader.vue';
|
||||||
import ImportTypePicker from '@/composables/ImportTypePicker.vue';
|
import ImportTypePicker from '@/composables/ImportTypePicker.vue';
|
||||||
import PopupElement from '@/components/PopupElement.vue';
|
import PopupElement from './PopupElement.vue';
|
||||||
import player from '@/ts/player';
|
import player from '@/ts/player';
|
||||||
import {
|
import {
|
||||||
sources
|
sources
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
onMounted,
|
onMounted,
|
||||||
useTemplateRef
|
useTemplateRef
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import PopupElement from '@/components/PopupElement.vue';
|
import PopupElement from '@/components/popups/PopupElement.vue';
|
||||||
import player from '@/ts/player';
|
import player from '@/ts/player';
|
||||||
import {
|
import {
|
||||||
queue
|
queue
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
ref,
|
ref,
|
||||||
useTemplateRef
|
useTemplateRef
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import PopupElement from '@/components/PopupElement.vue';
|
import PopupElement from '@/components/popups/PopupElement.vue';
|
||||||
|
|
||||||
const fileinput = useTemplateRef( 'fileinput' );
|
const fileinput = useTemplateRef( 'fileinput' );
|
||||||
const progress = ref( -1 );
|
const progress = ref( -1 );
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
importers,
|
importers,
|
||||||
isShowingImportTypePicker
|
isShowingImportTypePicker
|
||||||
} from './importTypePicker';
|
} from './importTypePicker';
|
||||||
import PopupElement from '@/components/PopupElement.vue';
|
import PopupElement from '@/components/popups/PopupElement.vue';
|
||||||
import SearchView from './SearchView.vue';
|
import SearchView from './SearchView.vue';
|
||||||
import {
|
import {
|
||||||
openDiskLoaderInterface
|
openDiskLoaderInterface
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
results,
|
results,
|
||||||
searchOpts
|
searchOpts
|
||||||
} from './searchManager';
|
} from './searchManager';
|
||||||
import PopupElement from '@/components/PopupElement.vue';
|
import PopupElement from '@/components/popups/PopupElement.vue';
|
||||||
import {
|
import {
|
||||||
ref
|
ref
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
// NOTE: For re-associating files with details here, can use dropdown in UI
|
// NOTE: For re-associating files with details here, can use dropdown in UI
|
||||||
|
|
||||||
export type Playlist = Song[];
|
export type PlaylistSongs = Song[];
|
||||||
|
|
||||||
export interface UrlToFileMapping {
|
export interface UrlToFileMapping {
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
queue,
|
queue,
|
||||||
queueIdx,
|
queueIdx,
|
||||||
rawQueue,
|
rawQueue,
|
||||||
|
repeat,
|
||||||
shuffle,
|
shuffle,
|
||||||
sources
|
sources
|
||||||
} from '../state';
|
} from '../state';
|
||||||
@@ -27,6 +28,12 @@ export const clearQueue = () => {
|
|||||||
duration.value = -1;
|
duration.value = -1;
|
||||||
playbackPercentage.value = 1;
|
playbackPercentage.value = 1;
|
||||||
isPlaying.value = false;
|
isPlaying.value = false;
|
||||||
|
repeat.value = 'off';
|
||||||
|
shuffle.value = false;
|
||||||
|
|
||||||
|
for ( const song of queue.value ) {
|
||||||
|
sources[ song.source ]?.songUnload( song );
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import type {
|
|||||||
AssociationResult
|
AssociationResult
|
||||||
} from '../plugins/interface';
|
} from '../plugins/interface';
|
||||||
import type {
|
import type {
|
||||||
Playlist
|
PlaylistSongs
|
||||||
} from '@/ts/dtype/playlist';
|
} from '@/ts/dtype/playlist';
|
||||||
import {
|
import {
|
||||||
openAssociationManager
|
openAssociationManager
|
||||||
} from '@/composables/associationManager';
|
} from '@/composables/associationManager';
|
||||||
|
|
||||||
export const load = ( playlist: Playlist ) => {
|
export const load = ( playlist: PlaylistSongs ) => {
|
||||||
queue.value = playlist;
|
queue.value = playlist;
|
||||||
rawQueue.value = playlist;
|
rawQueue.value = playlist;
|
||||||
|
|
||||||
|
|||||||
+5
@@ -118,4 +118,9 @@ export interface PlayerSourcePlugin {
|
|||||||
* You may use the provided interface elements (search bar and popups) to e.g. ask if user wants to use a playlist, album, etc
|
* You may use the provided interface elements (search bar and popups) to e.g. ask if user wants to use a playlist, album, etc
|
||||||
*/
|
*/
|
||||||
'addSongsFromThisSource': ( cb: ( songs: Song[] ) => void ) => void;
|
'addSongsFromThisSource': ( cb: ( songs: Song[] ) => void ) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fully unload a song. This is called on clear of a playlist
|
||||||
|
*/
|
||||||
|
'songUnload': ( song: Song ) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import {
|
|||||||
updateIdentifiers
|
updateIdentifiers
|
||||||
} from './association';
|
} from './association';
|
||||||
import {
|
import {
|
||||||
load
|
load,
|
||||||
|
unload
|
||||||
} from './loader';
|
} from './loader';
|
||||||
import {
|
import {
|
||||||
ref
|
ref
|
||||||
@@ -38,6 +39,7 @@ export const useLocalPlayer: PlayerSourcePluginInitializer = async (): Promise<P
|
|||||||
'association': associate,
|
'association': associate,
|
||||||
'updateIdentifiers': updateIdentifiers,
|
'updateIdentifiers': updateIdentifiers,
|
||||||
'mime': 'audio/aac,audio/mpeg,audio/wav,audio/mp4,audio/ogg'
|
'mime': 'audio/aac,audio/mpeg,audio/wav,audio/mp4,audio/ogg'
|
||||||
}
|
},
|
||||||
|
'songUnload': unload
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,3 +75,7 @@ export const load = ( cb: ( songs: Song[] ) => void ) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const unload = async ( song: Song ) => {
|
||||||
|
URL.revokeObjectURL( song.identifier );
|
||||||
|
};
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ import type {
|
|||||||
PlayerSourcePlugin,
|
PlayerSourcePlugin,
|
||||||
PlayerSourcePluginInitializer
|
PlayerSourcePluginInitializer
|
||||||
} from '../interface';
|
} from '../interface';
|
||||||
import type {
|
|
||||||
Song
|
|
||||||
} from '@/ts/dtype/playlist';
|
|
||||||
import {
|
import {
|
||||||
addFromAppleMusic
|
addFromAppleMusic
|
||||||
} from './search';
|
} from './search';
|
||||||
@@ -57,7 +54,8 @@ export const useMusicKit: PlayerSourcePluginInitializer = ( storefront: string =
|
|||||||
'addSongsFromThisSource': addFromAppleMusic,
|
'addSongsFromThisSource': addFromAppleMusic,
|
||||||
'loading': {
|
'loading': {
|
||||||
'requiresLocalFiles': false
|
'requiresLocalFiles': false
|
||||||
}
|
},
|
||||||
|
'songUnload': async () => {}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
throw new Error( 'ERR_AUTH' );
|
throw new Error( 'ERR_AUTH' );
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type {
|
|||||||
PlayerSourcePlugin
|
PlayerSourcePlugin
|
||||||
} from './plugins/interface';
|
} from './plugins/interface';
|
||||||
import type {
|
import type {
|
||||||
Playlist
|
PlaylistSongs
|
||||||
} from '../dtype/playlist';
|
} from '../dtype/playlist';
|
||||||
import type {
|
import type {
|
||||||
RepeatMode
|
RepeatMode
|
||||||
@@ -24,11 +24,11 @@ export const sources: {
|
|||||||
|
|
||||||
export const currentSource = ref( '' );
|
export const currentSource = ref( '' );
|
||||||
|
|
||||||
export const rawQueue: Ref<Playlist> = ref( [] );
|
export const rawQueue: Ref<PlaylistSongs> = ref( [] );
|
||||||
|
|
||||||
export const queueIdx = ref( 0 );
|
export const queueIdx = ref( 0 );
|
||||||
|
|
||||||
export const queue: Ref<Playlist> = ref( [] );
|
export const queue: Ref<PlaylistSongs> = ref( [] );
|
||||||
|
|
||||||
export const isPlaying = ref( false );
|
export const isPlaying = ref( false );
|
||||||
|
|
||||||
|
|||||||
Vendored
+14
@@ -0,0 +1,14 @@
|
|||||||
|
import type {
|
||||||
|
PlaylistSongs
|
||||||
|
} from '../dtype/playlist';
|
||||||
|
|
||||||
|
interface UserPlaylistFile {
|
||||||
|
'version': '1',
|
||||||
|
'userid': string,
|
||||||
|
'playlists': Playlist[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Playlist {
|
||||||
|
'name': string;
|
||||||
|
'songs': PlaylistSongs;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import {
|
||||||
|
queue,
|
||||||
|
rawQueue,
|
||||||
|
shuffle
|
||||||
|
} from '../player/state';
|
||||||
|
|
||||||
|
const savePlaylist = () => {
|
||||||
|
rawQueue.value = queue.value;
|
||||||
|
shuffle.value = false;
|
||||||
|
};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import {
|
||||||
|
type Ref,
|
||||||
|
ref
|
||||||
|
} from 'vue';
|
||||||
|
import type {
|
||||||
|
PlaylistSongs
|
||||||
|
} from '../dtype/playlist';
|
||||||
|
|
||||||
|
const currentPlaylist = ref( '' );
|
||||||
|
const playlists: Ref<PlaylistSongs> = ref( [] );
|
||||||
@@ -1,28 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import PlayerWrapper from '@/components/main/PlayerWrapper.vue';
|
||||||
queue,
|
import PlaylistsComponent from '@/components/main/PlaylistsComponent.vue';
|
||||||
queueIdx
|
|
||||||
} from '@/ts/player/state';
|
|
||||||
import CurrentSong from '@/components/CurrentSong.vue';
|
|
||||||
import PlayerControls from '@/components/PlayerControls.vue';
|
|
||||||
import QueueViewer from '@/components/QueueViewer.vue';
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="main-app">
|
<div class="main-app">
|
||||||
<div
|
<PlaylistsComponent />
|
||||||
class="panel"
|
<PlayerWrapper />
|
||||||
>
|
|
||||||
<div style="margin-bottom: 20px; width: 100%;">
|
|
||||||
<CurrentSong
|
|
||||||
v-model="queue[queueIdx]"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<PlayerControls />
|
|
||||||
</div>
|
|
||||||
<div class="panel">
|
|
||||||
<QueueViewer />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -34,16 +19,5 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
.panel {
|
|
||||||
width: 45%;
|
|
||||||
margin-left: 2.5%;
|
|
||||||
margin-right: 2.5%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user