feat: song editor

This commit is contained in:
2026-09-08 09:52:40 +02:00
parent 6d9853f2c1
commit 047a3a8b29
16 changed files with 169 additions and 51 deletions
-2
View File
@@ -1,5 +1,3 @@
// NOTE: For re-associating files with details here, can use dropdown in UI
export interface Playlist {
'name': string;
'songs': PlaylistSongs;
+1 -2
View File
@@ -20,13 +20,12 @@ export const isConnected = ref( false );
export const useAntiTamper = ref( false );
// TODO: Anit-Tamper
// const antiTamperClients = [];
let connection: null | EventSource = null;
let retries = 0;
// TODO: Persist room name in local storage
const createRoom = async ( name: string, antiTamper: boolean ): Promise<boolean> => {
if ( !( /^[a-zA-Z0-9-]{3,20}$/ ).test( name ) ) return false;
+8 -4
View File
@@ -39,7 +39,9 @@ const next = () => {
};
const prev = () => {
// TODO: Back to beginning if more than some seconds have passed?
if ( playbackPercentage.value * duration.value > 7 )
return seekTo( 0 );
playIndex( ( queueIdx.value - 1 + queue.value.length ) % queue.value.length );
};
@@ -136,10 +138,12 @@ const addToSongList = ( songs: Song[] ) => {
/**
* Add songs to the playlist from given source
* @param source - The ID of the source to add from
* @param cb - A custom callback to be executed instead of the default, which adds to queue
* @param skipLogin - Whether to skip login checks
*/
const addSongFromSource = async ( source: string ): Promise<boolean> => {
if ( sources[source]!.authorized.value ) {
sources[source]!.addSongsFromThisSource( addToSongList );
const addSongFromSource = async ( source: string, cb?: ( songs: Song[] ) => void, skipLogin?: boolean ): Promise<boolean> => {
if ( sources[source]!.authorized.value || skipLogin ) {
sources[source]!.addSongsFromThisSource( cb ? cb : addToSongList, skipLogin ? 0 : undefined, skipLogin );
} else {
sources[source]!.login!();
-1
View File
@@ -42,7 +42,6 @@ export const load = ( playlist: PlaylistSongs ) => {
};
if ( needToLoadLocalSongs ) {
// FIXME: Combine mime types
const mime = Object.values( sources )
.map( src => {
return src.loading.requiresLocalFiles === true ? src.loading.mime : '';
+4 -1
View File
@@ -116,8 +116,11 @@ export interface PlayerSourcePlugin {
/**
* Called when user adds another song to the playlist via this source.
* You may use the provided interface elements (search bar and popups) to e.g. ask if user wants to use a playlist, album, etc
* @param cb - Added songs are the arguments
* @param kindIdx - The index in the list of import types that should be used without showing popup. If unset, will show the picker
* @param autoClose - Whether or not to automatically close the interface after a single pick
*/
'addSongsFromThisSource': ( cb: ( songs: Song[] ) => void ) => void;
'addSongsFromThisSource': ( cb: ( songs: Song[] ) => void, kindIdx?: number, autoClose?: boolean ) => void;
/**
* Fully unload a song. This is called on clear of a playlist
@@ -1,9 +1,13 @@
import {
type CloudImport,
openImportTypePicker
} from '@/composables/importTypePicker';
import type {
Song
} from '@/ts/dtype/playlist';
import {
openImportTypePicker
} from '@/composables/importTypePicker';
openSearchInterface
} from '@/composables/searchManager';
import {
searchPlaylists
} from './playlists';
@@ -11,23 +15,29 @@ import {
searchSongs
} from './songs';
export const addFromAppleMusic = async ( cb: ( songs: Song[] ) => void ): Promise<void> => {
export const addFromAppleMusic = async ( cb: ( songs: Song[] ) => void, kindIdx?: number, autoClose?: boolean ): Promise<void> => {
const songs = await searchSongs( cb );
const playlists = await searchPlaylists( cb );
openImportTypePicker( [
const kinds: CloudImport[] = [
{
'name': 'Songs',
'type': 'cloud',
'addSelected': songs.addSelected,
'search': songs.search,
'minChars': 3
'minChars': 3,
'autoClose': autoClose ?? false
},
{
'name': 'Playlists',
'type': 'cloud',
'addSelected': playlists.addSelected,
'search': playlists.search
'search': playlists.search,
'autoClose': autoClose ?? false
}
] );
];
if ( kindIdx === undefined )
openImportTypePicker( kinds );
else
openSearchInterface( kinds[kindIdx]! );
};