mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-10 04:30:25 +02:00
Add queue handler
Initial support for play "now", "next", and "last"
This commit is contained in:
@@ -20,7 +20,7 @@ mpv.start().catch((error: any) => {
|
|||||||
mpv.on('status', (status: any) => {
|
mpv.on('status', (status: any) => {
|
||||||
if (status.property === 'playlist-pos') {
|
if (status.property === 'playlist-pos') {
|
||||||
if (status.value !== 0) {
|
if (status.value !== 0) {
|
||||||
getMainWindow()?.webContents.send('renderer-player-set-queue-next');
|
getMainWindow()?.webContents.send('renderer-player-auto-next');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -88,7 +88,7 @@ ipcMain.on('player-seek-to', async (_event, time: number) => {
|
|||||||
await mpv.goToPosition(time);
|
await mpv.goToPosition(time);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sets the queue to the given data. Used when manually starting a song or using the next/prev buttons
|
// Sets the queue in position 0 and 1 to the given data. Used when manually starting a song or using the next/prev buttons
|
||||||
ipcMain.on('player-set-queue', async (_event, data: PlayerData) => {
|
ipcMain.on('player-set-queue', async (_event, data: PlayerData) => {
|
||||||
if (data.queue.current) {
|
if (data.queue.current) {
|
||||||
await mpv.load(data.queue.current.streamUrl, 'replace');
|
await mpv.load(data.queue.current.streamUrl, 'replace');
|
||||||
@@ -99,8 +99,26 @@ ipcMain.on('player-set-queue', async (_event, data: PlayerData) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sets the next song in the queue when reaching the end of the queue
|
// Replaces the queue in position 1 to the given data
|
||||||
ipcMain.on('player-set-queue-next', async (_event, data: PlayerData) => {
|
ipcMain.on('player-set-queue-next', async (_event, data: PlayerData) => {
|
||||||
|
const size = await mpv.getPlaylistSize();
|
||||||
|
|
||||||
|
if (size > 1) {
|
||||||
|
await mpv.playlistRemove(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.queue.next) {
|
||||||
|
await mpv.load(data.queue.next.streamUrl, 'append');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sets the next song in the queue when reaching the end of the queue
|
||||||
|
ipcMain.on('player-auto-next', async (_event, data: PlayerData) => {
|
||||||
|
// Always keep the current song as position 0 in the mpv queue
|
||||||
|
// This allows us to easily set update the next song in the queue without
|
||||||
|
// disturbing the currently playing song
|
||||||
|
await mpv.playlistRemove(0);
|
||||||
|
|
||||||
if (data.queue.next) {
|
if (data.queue.next) {
|
||||||
await mpv.load(data.queue.next.streamUrl, 'append');
|
await mpv.load(data.queue.next.streamUrl, 'append');
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -3,6 +3,9 @@ import { PlayerData } from '../renderer/store';
|
|||||||
|
|
||||||
contextBridge.exposeInMainWorld('electron', {
|
contextBridge.exposeInMainWorld('electron', {
|
||||||
ipcRenderer: {
|
ipcRenderer: {
|
||||||
|
PLAYER_AUTO_NEXT(data: PlayerData) {
|
||||||
|
ipcRenderer.send('player-auto-next', data);
|
||||||
|
},
|
||||||
PLAYER_CURRENT_TIME() {
|
PLAYER_CURRENT_TIME() {
|
||||||
ipcRenderer.send('player-current-time');
|
ipcRenderer.send('player-current-time');
|
||||||
},
|
},
|
||||||
@@ -39,6 +42,11 @@ contextBridge.exposeInMainWorld('electron', {
|
|||||||
PLAYER_VOLUME(value: number) {
|
PLAYER_VOLUME(value: number) {
|
||||||
ipcRenderer.send('player-volume', value);
|
ipcRenderer.send('player-volume', value);
|
||||||
},
|
},
|
||||||
|
RENDERER_PLAYER_AUTO_NEXT(
|
||||||
|
cb: (event: IpcRendererEvent, data: any) => void
|
||||||
|
) {
|
||||||
|
ipcRenderer.on('renderer-player-auto-next', cb);
|
||||||
|
},
|
||||||
RENDERER_PLAYER_CURRENT_TIME(
|
RENDERER_PLAYER_CURRENT_TIME(
|
||||||
cb: (event: IpcRendererEvent, data: any) => void
|
cb: (event: IpcRendererEvent, data: any) => void
|
||||||
) {
|
) {
|
||||||
@@ -50,11 +58,6 @@ contextBridge.exposeInMainWorld('electron', {
|
|||||||
RENDERER_PLAYER_PLAY(cb: (event: IpcRendererEvent, data: any) => void) {
|
RENDERER_PLAYER_PLAY(cb: (event: IpcRendererEvent, data: any) => void) {
|
||||||
ipcRenderer.on('renderer-player-play', cb);
|
ipcRenderer.on('renderer-player-play', cb);
|
||||||
},
|
},
|
||||||
RENDERER_PLAYER_SET_QUEUE_NEXT(
|
|
||||||
cb: (event: IpcRendererEvent, data: any) => void
|
|
||||||
) {
|
|
||||||
ipcRenderer.on('renderer-player-set-queue-next', cb);
|
|
||||||
},
|
|
||||||
RENDERER_PLAYER_STOP(cb: (event: IpcRendererEvent, data: any) => void) {
|
RENDERER_PLAYER_STOP(cb: (event: IpcRendererEvent, data: any) => void) {
|
||||||
ipcRenderer.on('renderer-player-stop', cb);
|
ipcRenderer.on('renderer-player-stop', cb);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { UnstyledButton, UnstyledButtonProps } from '@mantine/core';
|
import { Button, UnstyledButton, UnstyledButtonProps } from '@mantine/core';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { RiPlayFill } from 'react-icons/ri';
|
import { RiPlayFill } from 'react-icons/ri';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import { Play } from '../../../types';
|
||||||
|
|
||||||
type PlayButtonType = UnstyledButtonProps &
|
type PlayButtonType = UnstyledButtonProps &
|
||||||
React.ComponentPropsWithoutRef<'button'>;
|
React.ComponentPropsWithoutRef<'button'>;
|
||||||
@@ -13,7 +14,7 @@ const PlayButton = styled(UnstyledButton)<PlayButtonType>`
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
background-color: var(--primary-color);
|
border: 1px solid var(--primary-color);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
@@ -78,13 +79,43 @@ export const GridCardControls = ({
|
|||||||
id: itemData[cardControls.idProperty],
|
id: itemData[cardControls.idProperty],
|
||||||
type: cardControls.type,
|
type: cardControls.type,
|
||||||
},
|
},
|
||||||
|
play: Play.NOW,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RiPlayFill size={25} />
|
<RiPlayFill size={25} />
|
||||||
</PlayButton>
|
</PlayButton>
|
||||||
</CenterControls>
|
</CenterControls>
|
||||||
<BottomControls />
|
<BottomControls>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
handlePlayQueueAdd({
|
||||||
|
byItemType: {
|
||||||
|
endpoint: cardControls.endpoint,
|
||||||
|
id: itemData[cardControls.idProperty],
|
||||||
|
type: cardControls.type,
|
||||||
|
},
|
||||||
|
play: Play.NEXT,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
NEXT
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
handlePlayQueueAdd({
|
||||||
|
byItemType: {
|
||||||
|
endpoint: cardControls.endpoint,
|
||||||
|
id: itemData[cardControls.idProperty],
|
||||||
|
type: cardControls.type,
|
||||||
|
},
|
||||||
|
play: Play.LAST,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
LATER
|
||||||
|
</Button>
|
||||||
|
</BottomControls>
|
||||||
</GridCardControlsContainer>
|
</GridCardControlsContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const VirtualGridWrapper = ({
|
|||||||
refInstance: Ref<any>;
|
refInstance: Ref<any>;
|
||||||
rowCount: number;
|
rowCount: number;
|
||||||
}) => {
|
}) => {
|
||||||
const { handlePlayQueueAdd } = usePlayQueueHandler();
|
const handlePlayQueueAdd = usePlayQueueHandler();
|
||||||
|
|
||||||
const memo = useMemo(
|
const memo = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export const LibraryAlbumsRoute = () => {
|
|||||||
cardControls={{
|
cardControls={{
|
||||||
endpoint: albumsApi.getAlbum,
|
endpoint: albumsApi.getAlbum,
|
||||||
idProperty: 'id',
|
idProperty: 'id',
|
||||||
type: Item.Album,
|
type: Item.ALBUM,
|
||||||
}}
|
}}
|
||||||
cardRows={[
|
cardRows={[
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useQueryClient } from 'react-query';
|
|
||||||
import { Item, Play } from '../../../../types';
|
import { Item, Play } from '../../../../types';
|
||||||
|
import { albumsApi } from '../../../api/albumsApi';
|
||||||
import { usePlayerStore } from '../../../store';
|
import { usePlayerStore } from '../../../store';
|
||||||
import {
|
import {
|
||||||
getJellyfinStreamUrl,
|
getJellyfinStreamUrl,
|
||||||
@@ -8,54 +8,23 @@ import {
|
|||||||
} from '../../../utils';
|
} from '../../../utils';
|
||||||
import { mpvPlayer } from '../utils/mpvPlayer';
|
import { mpvPlayer } from '../utils/mpvPlayer';
|
||||||
|
|
||||||
const getEndpoint = (item: Item) => {
|
const getEndpointByItemType = (item: Item) => {
|
||||||
switch (item) {
|
switch (item) {
|
||||||
case Item.Album:
|
case Item.ALBUM:
|
||||||
return 'getAlbum';
|
return albumsApi.getAlbum;
|
||||||
case Item.Artist:
|
|
||||||
return 'getArtistSongs';
|
|
||||||
case Item.Playlist:
|
|
||||||
return 'getPlaylist';
|
|
||||||
default:
|
default:
|
||||||
return 'getAlbum';
|
return albumsApi.getAlbum;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const usePlayQueueHandler = () => {
|
export const usePlayQueueHandler = () => {
|
||||||
const queryClient = useQueryClient();
|
const addToQueue = usePlayerStore((state) => state.addToQueue);
|
||||||
const addQueue = usePlayerStore((state) => state.add);
|
|
||||||
|
|
||||||
// const dispatchSongsToQueue = useCallback(
|
|
||||||
// (entries: Song[], play: Play) => {
|
|
||||||
// const filteredSongs = filterPlayQueue(config.playback.filters, entries);
|
|
||||||
|
|
||||||
// if (play === Play.Play) {
|
|
||||||
// if (filteredSongs.entries.length > 0) {
|
|
||||||
// } else {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (play === Play.Next || play === Play.Later) {
|
|
||||||
// if (filteredSongs.entries.length > 0) {
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// notifyToast(
|
|
||||||
// 'info',
|
|
||||||
// getPlayedSongsNotification({
|
|
||||||
// ...filteredSongs.count,
|
|
||||||
// type: play === Play.Play ? 'play' : 'add',
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
// },
|
|
||||||
// [config.playback.filters, dispatch]
|
|
||||||
// );
|
|
||||||
|
|
||||||
const handlePlayQueueAdd = async (options: {
|
const handlePlayQueueAdd = async (options: {
|
||||||
byData?: any[];
|
byData?: any[];
|
||||||
byItemType?: {
|
byItemType?: {
|
||||||
endpoint: (params: Record<string, any>) => any;
|
endpoint: (params: Record<string, any>) => any;
|
||||||
id: string;
|
id: number;
|
||||||
type: Item;
|
type: Item;
|
||||||
};
|
};
|
||||||
play: Play;
|
play: Play;
|
||||||
@@ -71,11 +40,13 @@ export const usePlayQueueHandler = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (deviceId) {
|
if (deviceId) {
|
||||||
const data = await options.byItemType.endpoint({
|
const endpoint = getEndpointByItemType(options.byItemType.type);
|
||||||
|
|
||||||
|
const { data } = await endpoint({
|
||||||
id: options.byItemType.id,
|
id: options.byItemType.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const songs = data.data.songs.map((song) => {
|
const songs = data.songs.map((song) => {
|
||||||
const auth = getServerFolderAuth(serverUrl, song.serverFolderId);
|
const auth = getServerFolderAuth(serverUrl, song.serverFolderId);
|
||||||
|
|
||||||
if (auth) {
|
if (auth) {
|
||||||
@@ -106,30 +77,16 @@ export const usePlayQueueHandler = () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const pData = addQueue(songs);
|
const playerData = addToQueue(songs, options.play);
|
||||||
mpvPlayer.setQueue(pData);
|
|
||||||
}
|
|
||||||
|
|
||||||
// const data = await apiController({
|
if (options.play === Play.NEXT || options.play === Play.LAST) {
|
||||||
// args: { id: options.byItemType.id, musicFolder: options.musicFolder },
|
mpvPlayer.setQueueNext(playerData);
|
||||||
// endpoint:
|
} else {
|
||||||
// options.byItemType.endpoint || getEndpoint(options.byItemType.item),
|
mpvPlayer.setQueue(playerData);
|
||||||
// serverType: config.serverType,
|
}
|
||||||
// });
|
}
|
||||||
// if (options.byItemType.item === Item.Album) {
|
|
||||||
// queryClient.setQueryData(['album', options.byItemType.id], data);
|
|
||||||
// } else if (options.byItemType.item === Item.Artist) {
|
|
||||||
// queryClient.setQueryData(['artistSongs', options.byItemType.id], data);
|
|
||||||
// } else if (options.byItemType.item === Item.Playlist) {
|
|
||||||
// queryClient.setQueryData(['playlist', options.byItemType.id], data);
|
|
||||||
// }
|
|
||||||
// if (data?.song) {
|
|
||||||
// dispatchSongsToQueue(data.song, options.play);
|
|
||||||
// } else {
|
|
||||||
// dispatchSongsToQueue(data, options.play);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { handlePlayQueueAdd };
|
return handlePlayQueueAdd;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
|
// Other files:
|
||||||
|
// main/features/core/player/index.ts
|
||||||
|
// main/preload.ts
|
||||||
|
// renderer/preload.d.ts
|
||||||
|
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { PlayerData, usePlayerStore } from 'renderer/store';
|
import { PlayerData, usePlayerStore } from '../../../store';
|
||||||
|
|
||||||
const ipc = isElectron() ? window.electron.ipcRenderer : null;
|
const ipc = isElectron() ? window.electron.ipcRenderer : null;
|
||||||
|
|
||||||
@@ -19,6 +24,8 @@ const setQueue = (data: PlayerData) => ipc?.PLAYER_SET_QUEUE(data);
|
|||||||
|
|
||||||
const setQueueNext = (data: PlayerData) => ipc?.PLAYER_SET_QUEUE_NEXT(data);
|
const setQueueNext = (data: PlayerData) => ipc?.PLAYER_SET_QUEUE_NEXT(data);
|
||||||
|
|
||||||
|
const playerAutoNext = (data: PlayerData) => ipc?.PLAYER_AUTO_NEXT(data);
|
||||||
|
|
||||||
const seek = (seconds: number) => ipc?.PLAYER_SEEK(seconds);
|
const seek = (seconds: number) => ipc?.PLAYER_SEEK(seconds);
|
||||||
|
|
||||||
const seekTo = (seconds: number) => ipc?.PLAYER_SEEK_TO(seconds);
|
const seekTo = (seconds: number) => ipc?.PLAYER_SEEK_TO(seconds);
|
||||||
@@ -42,10 +49,10 @@ ipc?.RENDERER_PLAYER_STOP(() => setPause());
|
|||||||
|
|
||||||
ipc?.RENDERER_PLAYER_CURRENT_TIME((_event, time) => setCurrentTime(time));
|
ipc?.RENDERER_PLAYER_CURRENT_TIME((_event, time) => setCurrentTime(time));
|
||||||
|
|
||||||
ipc?.RENDERER_PLAYER_SET_QUEUE_NEXT(() => {
|
ipc?.RENDERER_PLAYER_AUTO_NEXT(() => {
|
||||||
const playerData = autoNext();
|
const playerData = autoNext();
|
||||||
if (playerData.queue.next) {
|
if (playerData.queue.next) {
|
||||||
setQueueNext(playerData);
|
playerAutoNext(playerData);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -55,6 +62,7 @@ export const mpvPlayer = {
|
|||||||
next,
|
next,
|
||||||
pause,
|
pause,
|
||||||
play,
|
play,
|
||||||
|
playerAutoNext,
|
||||||
previous,
|
previous,
|
||||||
seek,
|
seek,
|
||||||
seekTo,
|
seekTo,
|
||||||
|
|||||||
Vendored
+4
-3
@@ -5,6 +5,7 @@ declare global {
|
|||||||
interface Window {
|
interface Window {
|
||||||
electron: {
|
electron: {
|
||||||
ipcRenderer: {
|
ipcRenderer: {
|
||||||
|
PLAYER_AUTO_NEXT(data: PlayerData): void;
|
||||||
PLAYER_CURRENT_TIME(): void;
|
PLAYER_CURRENT_TIME(): void;
|
||||||
PLAYER_MUTE(): void;
|
PLAYER_MUTE(): void;
|
||||||
PLAYER_NEXT(): void;
|
PLAYER_NEXT(): void;
|
||||||
@@ -17,6 +18,9 @@ declare global {
|
|||||||
PLAYER_SET_QUEUE_NEXT(data: PlayerData): void;
|
PLAYER_SET_QUEUE_NEXT(data: PlayerData): void;
|
||||||
PLAYER_STOP(): void;
|
PLAYER_STOP(): void;
|
||||||
PLAYER_VOLUME(value: number): void;
|
PLAYER_VOLUME(value: number): void;
|
||||||
|
RENDERER_PLAYER_AUTO_NEXT(
|
||||||
|
cb: (event: IpcRendererEvent, data: any) => void
|
||||||
|
): void;
|
||||||
RENDERER_PLAYER_CURRENT_TIME(
|
RENDERER_PLAYER_CURRENT_TIME(
|
||||||
cb: (event: IpcRendererEvent, data: any) => void
|
cb: (event: IpcRendererEvent, data: any) => void
|
||||||
): void;
|
): void;
|
||||||
@@ -26,9 +30,6 @@ declare global {
|
|||||||
RENDERER_PLAYER_PLAY(
|
RENDERER_PLAYER_PLAY(
|
||||||
cb: (event: IpcRendererEvent, data: any) => void
|
cb: (event: IpcRendererEvent, data: any) => void
|
||||||
): void;
|
): void;
|
||||||
RENDERER_PLAYER_SET_QUEUE_NEXT(
|
|
||||||
cb: (event: IpcRendererEvent, data: any) => void
|
|
||||||
): void;
|
|
||||||
RENDERER_PLAYER_STOP(
|
RENDERER_PLAYER_STOP(
|
||||||
cb: (event: IpcRendererEvent, data: any) => void
|
cb: (event: IpcRendererEvent, data: any) => void
|
||||||
): void;
|
): void;
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
/* eslint-disable prefer-destructuring */
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import create from 'zustand';
|
import create from 'zustand';
|
||||||
import { devtools } from 'zustand/middleware';
|
import { devtools } from 'zustand/middleware';
|
||||||
import {
|
import {
|
||||||
|
Play,
|
||||||
CrossfadeStyle,
|
CrossfadeStyle,
|
||||||
PlaybackStyle,
|
PlaybackStyle,
|
||||||
PlaybackType,
|
PlaybackType,
|
||||||
@@ -55,7 +57,7 @@ export interface PlayerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PlayerSlice extends PlayerState {
|
export interface PlayerSlice extends PlayerState {
|
||||||
add: (songs: Song[]) => PlayerData;
|
addToQueue: (songs: Song[], type: Play) => PlayerData;
|
||||||
autoNext: () => PlayerData;
|
autoNext: () => PlayerData;
|
||||||
getPlayerData: () => PlayerData;
|
getPlayerData: () => PlayerData;
|
||||||
next: () => PlayerData;
|
next: () => PlayerData;
|
||||||
@@ -70,15 +72,37 @@ export interface PlayerSlice extends PlayerState {
|
|||||||
|
|
||||||
export const usePlayerStore = create<PlayerSlice>()(
|
export const usePlayerStore = create<PlayerSlice>()(
|
||||||
devtools((set, get) => ({
|
devtools((set, get) => ({
|
||||||
add: (songs) => {
|
addToQueue: (songs, type) => {
|
||||||
set(
|
if (type === Play.NOW) {
|
||||||
produce((state) => {
|
set(
|
||||||
state.queue.default = songs;
|
produce((state) => {
|
||||||
state.current.time = 0;
|
state.queue.default = songs;
|
||||||
state.current.player = 1;
|
state.current.time = 0;
|
||||||
state.current.song = state.queue.default[state.current.index];
|
state.current.player = 1;
|
||||||
})
|
state.current.index = 0;
|
||||||
);
|
state.current.song = songs[0];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (type === Play.LAST) {
|
||||||
|
set(
|
||||||
|
produce((state) => {
|
||||||
|
state.queue.default = [...get().queue.default, ...songs];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else if (type === Play.NEXT) {
|
||||||
|
const queue = get().queue.default;
|
||||||
|
const currentIndex = get().current.index;
|
||||||
|
|
||||||
|
set(
|
||||||
|
produce((state) => {
|
||||||
|
state.queue.default = [
|
||||||
|
...queue.slice(0, currentIndex + 1),
|
||||||
|
...songs,
|
||||||
|
...queue.slice(currentIndex + 1),
|
||||||
|
];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return get().getPlayerData();
|
return get().getPlayerData();
|
||||||
},
|
},
|
||||||
|
|||||||
+15
-17
@@ -11,12 +11,12 @@ export enum ServerType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum Item {
|
export enum Item {
|
||||||
Album = 'album',
|
ALBUM = 'album',
|
||||||
Artist = 'artist',
|
ARTIST = 'artist',
|
||||||
Folder = 'folder',
|
FOLDER = 'folder',
|
||||||
Genre = 'genre',
|
GENRE = 'genre',
|
||||||
Music = 'music',
|
PLAYLIST = 'playlist',
|
||||||
Playlist = 'playlist',
|
SONG = 'song',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PlayerStatus {
|
export enum PlayerStatus {
|
||||||
@@ -31,9 +31,9 @@ export enum PlayerRepeat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum Play {
|
export enum Play {
|
||||||
Later = 'later',
|
LAST = 'last',
|
||||||
Next = 'next',
|
NEXT = 'next',
|
||||||
Now = 'play',
|
NOW = 'now',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CrossfadeStyle {
|
export enum CrossfadeStyle {
|
||||||
@@ -55,8 +55,6 @@ export enum PlaybackType {
|
|||||||
Web = 'web',
|
Web = 'web',
|
||||||
}
|
}
|
||||||
|
|
||||||
// export type ServerType = Server.Subsonic | Server.Jellyfin;
|
|
||||||
|
|
||||||
export type APIEndpoints =
|
export type APIEndpoints =
|
||||||
| 'getPlaylist'
|
| 'getPlaylist'
|
||||||
| 'getPlaylists'
|
| 'getPlaylists'
|
||||||
@@ -126,7 +124,7 @@ export interface Album {
|
|||||||
songCount: number;
|
songCount: number;
|
||||||
starred?: string;
|
starred?: string;
|
||||||
title: string;
|
title: string;
|
||||||
type: Item.Album;
|
type: Item.ALBUM;
|
||||||
uniqueId: string;
|
uniqueId: string;
|
||||||
userRating?: number;
|
userRating?: number;
|
||||||
year?: number;
|
year?: number;
|
||||||
@@ -142,7 +140,7 @@ export interface Artist {
|
|||||||
info?: ArtistInfo;
|
info?: ArtistInfo;
|
||||||
starred?: string;
|
starred?: string;
|
||||||
title: string;
|
title: string;
|
||||||
type?: Item.Artist;
|
type?: Item.ARTIST;
|
||||||
uniqueId?: string;
|
uniqueId?: string;
|
||||||
userRating?: number;
|
userRating?: number;
|
||||||
}
|
}
|
||||||
@@ -160,7 +158,7 @@ export interface Folder {
|
|||||||
image: string;
|
image: string;
|
||||||
isDir?: boolean;
|
isDir?: boolean;
|
||||||
title: string;
|
title: string;
|
||||||
type: Item.Folder;
|
type: Item.FOLDER;
|
||||||
uniqueId: string;
|
uniqueId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +167,7 @@ export interface Genre {
|
|||||||
id: string;
|
id: string;
|
||||||
songCount?: number;
|
songCount?: number;
|
||||||
title: string;
|
title: string;
|
||||||
type?: Item.Genre;
|
type?: Item.GENRE;
|
||||||
uniqueId?: string;
|
uniqueId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +184,7 @@ export interface Playlist {
|
|||||||
song?: Song[];
|
song?: Song[];
|
||||||
songCount?: number;
|
songCount?: number;
|
||||||
title: string;
|
title: string;
|
||||||
type: Item.Playlist;
|
type: Item.PLAYLIST;
|
||||||
uniqueId: string;
|
uniqueId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +214,7 @@ export interface Song {
|
|||||||
suffix?: string;
|
suffix?: string;
|
||||||
title: string;
|
title: string;
|
||||||
track?: number;
|
track?: number;
|
||||||
type?: Item.Music;
|
type?: Item.SONG;
|
||||||
uniqueId?: string;
|
uniqueId?: string;
|
||||||
userRating?: number;
|
userRating?: number;
|
||||||
year?: number;
|
year?: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user