feat: prepare adding songs

This commit is contained in:
2026-09-01 09:33:05 +02:00
parent 72dada18eb
commit f3d9251eee
18 changed files with 186 additions and 7 deletions
+57
View File
@@ -0,0 +1,57 @@
<script setup lang="ts">
import PopupElement from '@/components/PopupElement.vue';
import player from '@/ts/player';
import {
sources
} from '@/ts/player/state';
const showPopup = defineModel<boolean>( {
'required': true
} );
const addSong = ( source: string ) => {
player.addSongFromSource( source );
showPopup.value = false;
};
</script>
<template>
<div>
<PopupElement v-model="showPopup" show-close>
<h1>Add Song</h1>
<div class="song-sources">
<div v-for="(source, index) in sources" :key="index" @click="() => addSong( source.id )">
<i v-if="source.icon" :class="['fa-solid', 'fa-' + source.icon]"></i>
<p>{{ source.name }}</p>
</div>
</div>
</PopupElement>
</div>
</template>
<style lang="scss" scoped>
.song-sources {
display: flex;
flex-wrap: wrap;
width: 50vw;
height: 35vh;
justify-content: center;
overflow-y: scroll;
overflow-x: hidden;
>div {
width: 32%;
margin: 0.5%;
height: 50%;
background-color: var(--accent-background);
border-radius: 20px;
display: flex;
justify-content: center;
align-items: center;
>.fa-solid {
font-size: 1.5rem;
}
}
}
</style>
+2 -1
View File
@@ -55,7 +55,7 @@
position: relative;
overflow-y: scroll;
display: block;
transition: transform 0.5s ease, opacity 0.5s ease;
transition: transform 0.25s ease, opacity 0.25s ease;
>.close-icon {
position: absolute;
@@ -74,6 +74,7 @@
.popup-main {
transform: scale(1.25);
opacity: 0;
transition: transform 0.15s ease, opacity 0.15s ease;
}
.popup-backdrop {
+10 -1
View File
@@ -1,20 +1,28 @@
<script setup lang="ts">
import AddSong from './AddSong.vue';
import SortableList from './SortableList.vue';
import {
beautifyTime
} from '@/ts/util/time';
import player from '@/ts/player';
import {
ref
} from 'vue';
const queue = player.queue;
const idx = player.queueIdx;
const isPlaying = player.isPlaying;
const showAddSong = ref( false );
const addSong = () => {
showAddSong.value = true;
};
</script>
<template>
<div>
<div>
<button>
<button @click="addSong">
<i class="fa-solid fa-plus"></i>
Add
</button>
@@ -32,6 +40,7 @@
Transmit
</button>
</div>
<AddSong v-model="showAddSong" />
<div>
<SortableList v-slot="{ item: song, index }" v-model="queue">
<div class="song-cover-wrapper">
View File
+17
View File
@@ -0,0 +1,17 @@
import type {
Song
} from '@/ts/dtype/playlist';
export interface CloudImport {
// TODO: Consider if should make selectable and display album / playlist contents to select specific songs
'name': string;
'kind': 'list' | 'nested-list';
'search': ( term: string ) => Promise<Song[]>;
'addSelected': ( index: number ) => Promise<Song[]>;
}
// TODO: Need to add a way to access Apple Music API everywhere (maybe configure MusicKitJS globally and consume it in its plugin later?)
export interface FileImport {
'name': string;
'process': ( files: File[] ) => Promise<Song[]>;
}
View File
+32
View File
@@ -16,6 +16,38 @@ export interface PlayerSourcePlugin {
*/
'id': string;
/**
* Font-Awesome icon name (without fa and needs to be of free icons
*/
'icon'?: string;
/**
* Describe how files are loaded here
*/
'loading': {
/**
* Set to true if the user needs to load files
*/
'requiresLocalFiles': true;
/**
* The allowed MIME types for loading associated files
*/
'MIMETypes': string;
/**
* Function used to associate the file to the songs of the playlist
* @param files - The files that were picked by the user
* @returns A promise resolving to an array of song identifiers (corresponding to Song.identifier, for association)
*/
'association': ( files: File[] ) => Promise<string[]>;
} | {
/**
* Set to true if the user needs to load files
*/
'requiresLocalFiles': false;
};
/**
* Implement any login flow here. Optional
*/
+7 -1
View File
@@ -22,6 +22,12 @@ export const useLocalPlayer: PlayerSourcePluginInitializer = async (): Promise<P
'pause': player.pause,
'stop': () => player.src = '',
// TODO: Implement
'addSongsFromThisSource': async () => []
'addSongsFromThisSource': async () => [],
'loading': {
'requiresLocalFiles': true,
'MIMETypes': 'audio/mp3,audio/wav',
// TODO: Implement
'association': async () => []
}
};
};
+7 -1
View File
@@ -3,6 +3,9 @@ import type {
PlayerSourcePlugin,
PlayerSourcePluginInitializer
} from '../interface';
import {
addFromAppleMusic
} from './search';
import {
musicKitPlayback
} from './playback';
@@ -49,7 +52,10 @@ export const useMusicKit: PlayerSourcePluginInitializer = ( storefront: string =
'seekTo': controls.seekTo,
'login': login,
// TODO: Implement
'addSongsFromThisSource': async () => []
'addSongsFromThisSource': async () => await addFromAppleMusic(),
'loading': {
'requiresLocalFiles': false
}
};
} else {
throw new Error( 'ERR_AUTH' );
@@ -0,0 +1,10 @@
import type {
MusicKitInstance
} from 'musickitjs-v3-types/MusicKitInstance';
export const searchAlbums = async ( instance: MusicKitInstance, term: string ) => {
const addSelected = ( idx: number ) => {
// TODO: Load album's songs and return them
Promise.resolve();
};
};
@@ -0,0 +1,10 @@
import type {
MusicKitInstance
} from 'musickitjs-v3-types/MusicKitInstance';
import type {
Song
} from '@/ts/dtype/playlist';
export const addFromAppleMusic = async ( instance: MusicKitInstance ): Promise<Song[]> => {
return [];
};
@@ -0,0 +1,10 @@
import type {
MusicKitInstance
} from 'musickitjs-v3-types/MusicKitInstance';
export const searchPlaylists = async ( instance: MusicKitInstance, userPlaylists: boolean = true ) => {
const addSelected = ( idx: number ) => {
// TODO: Load the playlist content and return songs
Promise.resolve();
};
};
@@ -0,0 +1,20 @@
import type {
MusicKitInstance
} from 'musickitjs-v3-types/MusicKitInstance';
export const seachSongs = async ( instance: MusicKitInstance ) => {
const search = async ( term: string ) => {
const params = {
'term': term,
'types': [ 'songs' ],
'l': 'en-us'
};
await instance.api.music( '/v1/catalog/{{storefrontId}}/search', params );
};
const addSelected = ( idx: number ) => {
// TODO: Need to only return the selected song
Promise.resolve();
};
};
-2
View File
@@ -1,7 +1,5 @@
<script setup lang="ts">
import QueueViewer from '@/components/QueueViewer.vue';
</script>
<template>