mirror of
https://github.com/Dangoware/dango-music-player.git
synced 2025-06-22 22:52:59 -05:00
added "Add to Playlist" button to context menu
This commit is contained in:
parent
99bf0b6855
commit
bc79718dd5
8 changed files with 118 additions and 38 deletions
|
@ -109,14 +109,12 @@ pub enum LibraryCommand {
|
|||
AllSongs,
|
||||
GetLibrary,
|
||||
ExternalPlaylist(Uuid),
|
||||
PlaylistSong{
|
||||
list_uuid: Uuid,
|
||||
item_uuid: Uuid
|
||||
},
|
||||
PlaylistSong { list_uuid: Uuid, item_uuid: Uuid },
|
||||
Playlist(Uuid),
|
||||
ImportM3UPlayList(PathBuf),
|
||||
Save,
|
||||
Playlists,
|
||||
PlaylistAddSong { playlist: Uuid, song: Uuid },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -74,6 +74,15 @@ impl ControllerHandle {
|
|||
Ok((uuid, name))
|
||||
}
|
||||
|
||||
pub async fn playlist_add_song(&self, playlist: Uuid, song: Uuid) {
|
||||
let (command, tx) =
|
||||
LibraryCommandInput::command(LibraryCommand::PlaylistAddSong { playlist, song });
|
||||
self.lib_mail_rx.send(command).await.unwrap();
|
||||
let LibraryResponse::Ok = tx.recv().await.unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
}
|
||||
|
||||
// The Queue Section
|
||||
pub async fn queue_append(
|
||||
&self,
|
||||
|
|
|
@ -89,15 +89,27 @@ impl Controller {
|
|||
.await
|
||||
.unwrap();
|
||||
}
|
||||
LibraryCommand::PlaylistSong { list_uuid, item_uuid } => {
|
||||
let playlist= library.playlists.query_uuid(&list_uuid).unwrap();
|
||||
let Some((uuid, index)) = playlist.query_uuid(&item_uuid) else { todo!() };
|
||||
let Some((song, _)) = library.query_uuid(uuid) else { todo!() };
|
||||
LibraryCommand::PlaylistSong {
|
||||
list_uuid,
|
||||
item_uuid,
|
||||
} => {
|
||||
let playlist = library.playlists.query_uuid(&list_uuid).unwrap();
|
||||
let Some((uuid, index)) = playlist.query_uuid(&item_uuid) else {
|
||||
todo!()
|
||||
};
|
||||
let Some((song, _)) = library.query_uuid(uuid) else {
|
||||
todo!()
|
||||
};
|
||||
res_rx
|
||||
.send(LibraryResponse::PlaylistSong(song.clone(), index))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
LibraryCommand::PlaylistAddSong { playlist, song } => {
|
||||
let playlist = library.query_playlist_uuid_mut(&playlist).unwrap();
|
||||
playlist.add_track(song);
|
||||
res_rx.send(LibraryResponse::Ok).await.unwrap();
|
||||
}
|
||||
_ => {
|
||||
todo!()
|
||||
}
|
||||
|
|
|
@ -1232,6 +1232,10 @@ impl MusicLibrary {
|
|||
self.playlists.query_uuid(uuid)
|
||||
}
|
||||
|
||||
pub fn query_playlist_uuid_mut(&mut self, uuid: &Uuid) -> Option<&mut Playlist> {
|
||||
self.playlists.query_uuid_mut(uuid)
|
||||
}
|
||||
|
||||
pub fn push_playlist(&mut self, playlist: PlaylistFolderItem) {
|
||||
self.playlists.items.push(playlist);
|
||||
}
|
||||
|
|
|
@ -55,6 +55,20 @@ impl PlaylistFolder {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn query_uuid_mut(&mut self, uuid: &Uuid) -> Option<&mut Playlist> {
|
||||
for item in &mut self.items {
|
||||
match item {
|
||||
PlaylistFolderItem::Folder(folder) => return folder.query_uuid_mut(uuid),
|
||||
PlaylistFolderItem::List(playlist) => {
|
||||
if &playlist.uuid == uuid {
|
||||
return Some(playlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn lists_recursive(&self) -> Vec<&Playlist> {
|
||||
let mut vec = vec![];
|
||||
for item in &self.items {
|
||||
|
|
|
@ -24,8 +24,8 @@ use uuid::Uuid;
|
|||
use wrappers::{_Song, stop};
|
||||
|
||||
use crate::wrappers::{
|
||||
get_library, get_playlist, get_playlists, get_queue, get_song, import_playlist, next, pause,
|
||||
play, prev, remove_from_queue, seek, set_volume,
|
||||
add_song_to_playlist, get_library, get_playlist, get_playlists, get_queue, get_song,
|
||||
import_playlist, next, pause, play, prev, remove_from_queue, seek, set_volume,
|
||||
};
|
||||
use commands::{add_song_to_queue, display_album_art, last_fm_init_auth, play_now};
|
||||
|
||||
|
@ -69,6 +69,7 @@ pub fn run() {
|
|||
save_config,
|
||||
close_window,
|
||||
start_controller,
|
||||
add_song_to_playlist,
|
||||
// test_menu,
|
||||
])
|
||||
.manage(tempfile::TempDir::new().unwrap())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::BTreeMap, path::PathBuf};
|
||||
use std::{collections::BTreeMap, path::PathBuf, thread::spawn};
|
||||
|
||||
use chrono::{serde::ts_milliseconds_option, DateTime, Utc};
|
||||
use crossbeam::channel::Sender;
|
||||
|
@ -11,6 +11,7 @@ use dmp_core::{
|
|||
};
|
||||
use itertools::Itertools;
|
||||
use serde::Serialize;
|
||||
|
||||
use tauri::{AppHandle, Emitter, State, Wry};
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -217,6 +218,8 @@ pub async fn get_playlists(
|
|||
ctrl_handle: State<'_, ControllerHandle>,
|
||||
) -> Result<(), String> {
|
||||
let lists = ctrl_handle.playlist_get_all().await;
|
||||
spawn(move || {
|
||||
futures::executor::block_on(async {
|
||||
app.emit(
|
||||
"playlists_gotten",
|
||||
lists
|
||||
|
@ -225,6 +228,8 @@ pub async fn get_playlists(
|
|||
.collect_vec(),
|
||||
)
|
||||
.unwrap();
|
||||
})
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -272,3 +277,12 @@ pub async fn get_song(
|
|||
pub async fn seek(ctrl_handle: State<'_, ControllerHandle>, time: i64) -> Result<(), String> {
|
||||
ctrl_handle.seek(time).await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn add_song_to_playlist(
|
||||
ctrl_handle: State<'_, ControllerHandle>,
|
||||
song: Uuid,
|
||||
playlist: Uuid,
|
||||
) -> Result<(), String> {
|
||||
Ok(ctrl_handle.playlist_add_song(playlist, song).await)
|
||||
}
|
||||
|
|
66
src/App.tsx
66
src/App.tsx
|
@ -1,4 +1,4 @@
|
|||
import React, { createRef, ReactEventHandler, useEffect, useRef, useState } from "react";
|
||||
import React, { createRef, MutableRefObject, ReactEventHandler, useEffect, useRef, useState } from "react";
|
||||
import { convertFileSrc, invoke } from "@tauri-apps/api/core";
|
||||
import "./App.css";
|
||||
import { Config, playbackInfo } from "./types";
|
||||
|
@ -7,7 +7,7 @@ import { Config, playbackInfo } from "./types";
|
|||
// import { fetch } from "@tauri-apps/plugin-http";
|
||||
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
|
||||
import { getCurrentWindow, LogicalPosition } from "@tauri-apps/api/window";
|
||||
import { Menu } from "@tauri-apps/api/menu";
|
||||
import { Menu, Submenu, SubmenuOptions } from "@tauri-apps/api/menu";
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
|
||||
const appWindow = getCurrentWebviewWindow();
|
||||
|
@ -18,6 +18,7 @@ function App() {
|
|||
const [playing, setPlaying] = useState(false);
|
||||
const [playlists, setPlaylists] = useState<JSX.Element[]>([]);
|
||||
const [viewName, setViewName] = useState("Library");
|
||||
const playlistsInfo= useRef<PlaylistInfo[]>([]);
|
||||
|
||||
const [nowPlaying, setNowPlaying] = useState<JSX.Element>(
|
||||
<NowPlaying
|
||||
|
@ -83,8 +84,8 @@ function App() {
|
|||
<main>
|
||||
<div className="container">
|
||||
<div className="leftSide">
|
||||
<PlaylistHead playlists={ playlists } setPlaylists={ setPlaylists } setViewName={ setViewName } setLibrary={ library[1] } />
|
||||
<MainView lib_ref={ library } viewName={ viewName } />
|
||||
<PlaylistHead playlists={ playlists } setPlaylists={ setPlaylists } setViewName={ setViewName } setLibrary={ library[1] } playlistsInfo={ playlistsInfo }/>
|
||||
<MainView lib_ref={ library } viewName={ viewName } playlistsInfo={ playlistsInfo } />
|
||||
</div>
|
||||
<div className="rightSide">
|
||||
{ nowPlaying }
|
||||
|
@ -100,22 +101,32 @@ function App() {
|
|||
|
||||
export default App;
|
||||
|
||||
interface PlaylistInfo {
|
||||
uuid: string,
|
||||
name: string,
|
||||
}
|
||||
|
||||
|
||||
interface PlaylistHeadProps {
|
||||
playlists: JSX.Element[]
|
||||
setPlaylists: React.Dispatch<React.SetStateAction<JSX.Element[]>>,
|
||||
setViewName: React.Dispatch<React.SetStateAction<string>>,
|
||||
setLibrary: React.Dispatch<React.SetStateAction<JSX.Element[]>>,
|
||||
playlistsInfo: MutableRefObject<PlaylistInfo[]>,
|
||||
}
|
||||
|
||||
function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary }: PlaylistHeadProps) {
|
||||
function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary, playlistsInfo }: PlaylistHeadProps) {
|
||||
|
||||
useEffect(() => {
|
||||
const unlisten = appWindow.listen<any[]>("playlists_gotten", (_res) => {
|
||||
// console.log(event);
|
||||
let res = _res.payload;
|
||||
let res = _res.payload as PlaylistInfo[];
|
||||
playlistsInfo.current = [...res];
|
||||
console.log(playlistsInfo, res);
|
||||
|
||||
setPlaylists([
|
||||
...res.map( (item) => {
|
||||
|
||||
return (
|
||||
<button onClick={ () => {
|
||||
invoke('get_playlist', { uuid: item.uuid }).then((list) => {
|
||||
|
@ -130,6 +141,7 @@ function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary }: Play
|
|||
plays={ song.plays }
|
||||
duration={ song.duration }
|
||||
tags={ song.tags }
|
||||
playlists={ playlistsInfo }
|
||||
/>
|
||||
)
|
||||
})])
|
||||
|
@ -163,6 +175,7 @@ function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary }: Play
|
|||
plays={ song.plays }
|
||||
duration={ song.duration }
|
||||
tags={ song.tags }
|
||||
playlists={ playlistsInfo }
|
||||
/>
|
||||
)
|
||||
})])
|
||||
|
@ -190,6 +203,7 @@ function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary }: Play
|
|||
plays={ song.plays }
|
||||
duration={ song.duration }
|
||||
tags={ song.tags }
|
||||
playlists={ playlistsInfo }
|
||||
/>
|
||||
)
|
||||
})])
|
||||
|
@ -204,10 +218,11 @@ function PlaylistHead({ playlists, setPlaylists, setViewName, setLibrary }: Play
|
|||
|
||||
interface MainViewProps {
|
||||
lib_ref: [JSX.Element[], React.Dispatch<React.SetStateAction<JSX.Element[]>>],
|
||||
viewName: string
|
||||
viewName: string,
|
||||
playlistsInfo: MutableRefObject<PlaylistInfo[]>,
|
||||
}
|
||||
|
||||
function MainView({ lib_ref, viewName }: MainViewProps) {
|
||||
function MainView({ lib_ref, viewName, playlistsInfo }: MainViewProps) {
|
||||
const [library, setLibrary] = lib_ref;
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -225,6 +240,7 @@ function MainView({ lib_ref, viewName }: MainViewProps) {
|
|||
plays={ song.plays }
|
||||
duration={ song.duration }
|
||||
tags={ song.tags }
|
||||
playlists={ playlistsInfo }
|
||||
/>
|
||||
)
|
||||
})])
|
||||
|
@ -255,7 +271,8 @@ interface SongProps {
|
|||
last_played?: string,
|
||||
date_added?: string,
|
||||
date_modified?: string,
|
||||
tags: any
|
||||
tags: any,
|
||||
playlists: MutableRefObject<PlaylistInfo[]>
|
||||
}
|
||||
|
||||
function Song(props: SongProps) {
|
||||
|
@ -264,15 +281,27 @@ function Song(props: SongProps) {
|
|||
invoke('add_song_to_queue', { uuid: props.uuid, location: props.playerLocation }).then(() => {});
|
||||
}
|
||||
|
||||
const songMenuPromise = Menu.new({
|
||||
items: [
|
||||
{ id: "add_song_to_queue" + props.uuid, text: "Add to Queue", action: add_to_queue_test}
|
||||
]
|
||||
})
|
||||
|
||||
async function clickHandler(event: React.MouseEvent) {
|
||||
event.preventDefault();
|
||||
const menu = await songMenuPromise;
|
||||
console.log(props.playlists);
|
||||
const _ = await invoke('get_playlists');
|
||||
const menu = await Menu.new({
|
||||
items: [
|
||||
{ id: "add_song_to_queue" + props.uuid, text: "Add to Queue", action: add_to_queue_test },
|
||||
await Submenu.new(
|
||||
{
|
||||
text: "Add to Playlist...",
|
||||
items: [...props.playlists.current.map((list) => {
|
||||
const addToPlaylist = () => {
|
||||
invoke('add_song_to_playlist', { playlist: list.uuid, song: props.uuid }).then(() => {});
|
||||
}
|
||||
return { id: "add_song_to_playlists" + props.uuid + list.uuid, text: list.name, action: addToPlaylist }
|
||||
})]
|
||||
} as SubmenuOptions
|
||||
)
|
||||
]
|
||||
})
|
||||
;
|
||||
const pos = new LogicalPosition(event.clientX, event.clientY);
|
||||
menu.popup(pos);
|
||||
}
|
||||
|
@ -419,11 +448,10 @@ interface QueueSongProps {
|
|||
function QueueSong({ song, location, index }: QueueSongProps) {
|
||||
// console.log(song.tags);
|
||||
|
||||
let removeFromQueue = () => {
|
||||
const removeFromQueue = () => {
|
||||
invoke('remove_from_queue', { index: index }).then(() => {})
|
||||
}
|
||||
|
||||
let playNow = () => {
|
||||
const playNow = () => {
|
||||
invoke('play_now', { uuid: song.uuid, location: location }).then(() => {})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue