mirror of
https://github.com/janishutz/MusicPlayer.git
synced 2026-09-10 14:25:24 +02:00
feat: basic player commands
This commit is contained in:
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
export type RepeatMode = 'off' | 'one' | 'all';
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
import {
|
||||||
|
currentSource,
|
||||||
|
queueIdx,
|
||||||
|
repeat,
|
||||||
|
shuffle,
|
||||||
|
sources
|
||||||
|
} from './state';
|
||||||
|
import {
|
||||||
|
startTracking,
|
||||||
|
stopTracking
|
||||||
|
} from './status-tracking';
|
||||||
|
import type {
|
||||||
|
RepeatMode
|
||||||
|
} from '../dtype/player';
|
||||||
|
import {
|
||||||
|
addSongList
|
||||||
|
} from './playlists/add';
|
||||||
|
import {
|
||||||
|
playIndex
|
||||||
|
} from './playlists';
|
||||||
|
|
||||||
|
const next = () => {
|
||||||
|
playIndex( queueIdx.value + 1 );
|
||||||
|
};
|
||||||
|
|
||||||
|
const prev = () => {
|
||||||
|
// TODO: Back to beginning if more than some seconds have passed?
|
||||||
|
playIndex( queueIdx.value - 1 );
|
||||||
|
};
|
||||||
|
|
||||||
|
const play = () => {
|
||||||
|
if ( currentSource.value === '' ) return;
|
||||||
|
|
||||||
|
sources[currentSource.value]?.play();
|
||||||
|
startTracking();
|
||||||
|
};
|
||||||
|
|
||||||
|
const pause = () => {
|
||||||
|
if ( currentSource.value === '' ) return;
|
||||||
|
|
||||||
|
sources[currentSource.value]?.pause();
|
||||||
|
stopTracking();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seek to a specific point in the song
|
||||||
|
* @param pos - Percentage of song, value in [0, 1]
|
||||||
|
*/
|
||||||
|
const seekTo = ( pos: number ) => {
|
||||||
|
if ( currentSource.value === '' ) return;
|
||||||
|
|
||||||
|
sources[currentSource.value]?.seekTo( pos );
|
||||||
|
};
|
||||||
|
|
||||||
|
const skip10 = () => {
|
||||||
|
if ( currentSource.value === '' ) return;
|
||||||
|
|
||||||
|
const source = sources[currentSource.value];
|
||||||
|
const duration = source?.getDuration() ?? -1;
|
||||||
|
|
||||||
|
seekTo( ( ( ( source?.getPlaybackPos() ?? 0 ) * duration ) + 10 ) / duration );
|
||||||
|
};
|
||||||
|
|
||||||
|
const back10 = () => {
|
||||||
|
if ( currentSource.value === '' ) return;
|
||||||
|
|
||||||
|
const source = sources[currentSource.value];
|
||||||
|
const duration = source?.getDuration() ?? -1;
|
||||||
|
|
||||||
|
seekTo( ( ( ( source?.getPlaybackPos() ?? 0 ) * duration ) - 10 ) / duration );
|
||||||
|
};
|
||||||
|
|
||||||
|
const setShuffle = ( enabled: boolean ) => {
|
||||||
|
// TODO: Shuffle the order
|
||||||
|
shuffle.value = enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setRepeat = ( mode: RepeatMode ) => {
|
||||||
|
repeat.value = mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSources = (): string[] => {
|
||||||
|
return Object.keys( sources );
|
||||||
|
};
|
||||||
|
|
||||||
|
const addSongFromSource = async ( source: string ) => {
|
||||||
|
addSongList( await sources[source]!.addSongsFromThisSource() );
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
play,
|
||||||
|
pause,
|
||||||
|
seekTo,
|
||||||
|
skip10,
|
||||||
|
back10,
|
||||||
|
setShuffle,
|
||||||
|
setRepeat,
|
||||||
|
playIndex,
|
||||||
|
getSources,
|
||||||
|
addSongFromSource,
|
||||||
|
next,
|
||||||
|
prev
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import type {
|
||||||
|
Song
|
||||||
|
} from '@/ts/dtype/playlist';
|
||||||
|
import {
|
||||||
|
queue
|
||||||
|
} from '../state';
|
||||||
|
|
||||||
|
export const addSongList = ( songs: Song[] ) => {
|
||||||
|
for ( const song of songs ) {
|
||||||
|
queue.value.push( song );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const shuffleList = () => {};
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
currentSource,
|
||||||
|
queue,
|
||||||
|
queueIdx,
|
||||||
|
repeat,
|
||||||
|
sources
|
||||||
|
} from '../state';
|
||||||
|
import {
|
||||||
|
startTracking,
|
||||||
|
stopTracking
|
||||||
|
} from '../status-tracking';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play a song at the given index of the queue. Wraps to 0 and end if index below 0 or above end
|
||||||
|
* @param idx - The index in the queue to play at
|
||||||
|
*/
|
||||||
|
export const playIndex = async ( idx: number ) => {
|
||||||
|
if ( idx >= queue.value.length ) {
|
||||||
|
idx = repeat.value === 'all' ? 0 : -1;
|
||||||
|
} else if ( idx < 0 ) {
|
||||||
|
idx = repeat.value === 'all' ? queue.value.length - 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( idx < 0 ) return false;
|
||||||
|
|
||||||
|
sources[currentSource.value]?.stop();
|
||||||
|
stopTracking();
|
||||||
|
|
||||||
|
const nextSong = queue.value[ idx ]!;
|
||||||
|
|
||||||
|
queueIdx.value = idx;
|
||||||
|
currentSource.value = nextSong.source;
|
||||||
|
sources[currentSource.value]?.playSong( nextSong.identifier );
|
||||||
|
|
||||||
|
startTracking();
|
||||||
|
};
|
||||||
@@ -1 +1,2 @@
|
|||||||
// TODO: If local files contained, prompt user to select files, then associate based on file names
|
// TODO: If local files contained, prompt user to select files, then associate based on file names
|
||||||
|
export const load = () => {};
|
||||||
+1
-1
@@ -22,7 +22,7 @@ export interface PlayerSourcePlugin {
|
|||||||
'login'?: () => Promise<boolean>;
|
'login'?: () => Promise<boolean>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Play a song by its ID
|
* Play a song by its ID. Should start playing it immediately
|
||||||
*/
|
*/
|
||||||
'playSong': ( id: string ) => Promise<void>;
|
'playSong': ( id: string ) => Promise<void>;
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,21 @@ import {
|
|||||||
type Ref,
|
type Ref,
|
||||||
ref
|
ref
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
|
import type {
|
||||||
|
PlayerSourcePlugin
|
||||||
|
} from './plugins/interface';
|
||||||
import type {
|
import type {
|
||||||
Playlist
|
Playlist
|
||||||
} from '../dtype/playlist';
|
} from '../dtype/playlist';
|
||||||
|
import type {
|
||||||
|
RepeatMode
|
||||||
|
} from '../dtype/player';
|
||||||
|
|
||||||
|
export const sources: {
|
||||||
|
[key: string]: PlayerSourcePlugin
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
export const currentSource = ref( '' );
|
||||||
|
|
||||||
export const queueIdx = ref( 0 );
|
export const queueIdx = ref( 0 );
|
||||||
|
|
||||||
@@ -14,7 +26,7 @@ export const isPlaying = ref( false );
|
|||||||
|
|
||||||
export const shuffle = ref( false );
|
export const shuffle = ref( false );
|
||||||
|
|
||||||
export const repeat: Ref<'off' | 'one' | 'all'> = ref( 'off' );
|
export const repeat: Ref<RepeatMode> = ref( 'off' );
|
||||||
|
|
||||||
export const playbackPercentage = ref( 0 );
|
export const playbackPercentage = ref( 0 );
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// Tracking of time and player status
|
||||||
|
export const startTracking = () => {};
|
||||||
|
|
||||||
|
export const stopTracking = () => {};
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
# Frontend
|
||||||
|
- [ ] Status tracking (position, etc)
|
||||||
|
- [ ] Change metadata association for automated association
|
||||||
|
- [ ] Move songs with D&D, or index assignment?
|
||||||
|
- [ ] Explain additional info
|
||||||
|
- [ ] Tour
|
||||||
|
- [ ] Get player working
|
||||||
|
- [ ] Save playlists locally or on backend
|
||||||
|
- [ ] SSE connection
|
||||||
|
- [ ] Anti-Tamper
|
||||||
|
- [ ] Remote showcase views
|
||||||
|
- [ ] Login
|
||||||
|
- [ ] About page
|
||||||
|
- [ ] Apple-Music signin
|
||||||
|
- [ ] Loading existing playlists with local music and association of that
|
||||||
|
|
||||||
|
# Backend
|
||||||
|
- [ ] Implement all endpoints
|
||||||
|
- [ ] Integrate with store and auth sdks
|
||||||
|
- [ ] Create FOSS solution for above (single user)
|
||||||
|
- [ ] Setup guide
|
||||||
|
- [ ] Docker containers
|
||||||
|
- [ ] CI
|
||||||
Reference in New Issue
Block a user