From 6a247310a687b636161c49214fd90618da2d5271 Mon Sep 17 00:00:00 2001 From: Kubat <maelle.martin@proton.me> Date: Tue, 3 Dec 2024 19:42:21 +0100 Subject: [PATCH] NETWORK: Handle playlists messages - Query things by id and not names - Modify LKT - Handle things in amadeus --- amadeus/src/app.rs | 81 ++++++++++++++++++--------- amadeus/src/playlist.rs | 12 ++-- amadeus/src/store.rs | 23 ++++++-- lektor_lib/src/requests.rs | 14 +++-- lektor_nkdb/src/playlists/playlist.rs | 25 +++++---- lektor_payloads/src/action.rs | 3 + lektord/src/app/routes.rs | 21 ++++--- lkt/src/lib.rs | 18 ++++-- 8 files changed, 133 insertions(+), 64 deletions(-) diff --git a/amadeus/src/app.rs b/amadeus/src/app.rs index ba976dc9..e40eda13 100644 --- a/amadeus/src/app.rs +++ b/amadeus/src/app.rs @@ -33,7 +33,9 @@ use futures::{ stream::{self, FuturesUnordered}, }; use lektor_lib::*; -use lektor_payloads::{Epochs, KId, Kara, Priority, SearchFrom, PRIORITY_LENGTH, PRIORITY_VALUES}; +use lektor_payloads::{ + Epochs, KId, Kara, PlaylistInfo, Priority, SearchFrom, PRIORITY_LENGTH, PRIORITY_VALUES, +}; use lektor_utils::{config::SocketScheme, open}; use std::{ borrow::Cow, @@ -167,6 +169,7 @@ pub enum LektordMessage { ChangedQueueLevel(Priority, Vec<KId>), ChangedHistory(Vec<KId>), ChangedAvailablePlaylists(Vec<KId>), + ChangedAvailablePlaylistInfos(PlaylistInfo), ChangedPlaylistContent(KId, Vec<KId>), ChangedPlaylistsContent(Vec<(KId, Vec<KId>)>), } @@ -612,26 +615,18 @@ impl AppModel { // Down here, got updates from lektord. LektordMessage::ChangedAvailablePlaylists(names) => { - let config = self.connect_config.clone(); - let playlists = self.store.keep_playlists(names); - Task::future(async move { - let updated_playlists = stream::iter(playlists) - .zip(stream::repeat_with(move || config.clone())) - .filter_map(|(id, config)| async move { - let config = config.read().await; - requests::get_playlist_content(config.as_ref(), id) - .await - .map(|content| (id, content)) - .ok() - }) - .collect::<FuturesUnordered<_>>() - .await; - cosmic::app::Message::App(Message::LektordUpdate( - LektordMessage::ChangedPlaylistsContent(Vec::from_iter(updated_playlists)), - )) - }) + let playlists = self.store.keep_playlists(&names); + self.update_playlists_content(playlists) } + LektordMessage::ChangedAvailablePlaylistInfos(infos) => (self.store) + .set_playlist_infos(infos) + .map_or_else(Task::none, |plt_id| { + Task::done(cosmic::app::Message::App(Message::SendCommand( + LektordCommand::PlaylistGetContent(plt_id), + ))) + }), + LektordMessage::ChangedHistory(kids) => { self.store.set_history(kids); Task::none() @@ -662,6 +657,28 @@ impl AppModel { } } + /// Update playlists' content. + fn update_playlists_content(&mut self, playlists: Vec<KId>) -> Task<Message> { + let config = self.connect_config.clone(); + + Task::future(async move { + let updated_playlists = stream::iter(playlists) + .zip(stream::repeat_with(move || config.clone())) + .filter_map(|(id, config)| async move { + requests::get_playlist_content(config.read().await.as_ref(), id) + .await + .map(|content| (id, content)) + .ok() + }) + .collect::<FuturesUnordered<_>>() + .await; + + cosmic::app::Message::App(Message::LektordUpdate( + LektordMessage::ChangedPlaylistsContent(Vec::from_iter(updated_playlists)), + )) + }) + } + /// Send commands to lektord. fn send_command(&mut self, cmd: LektordCommand) -> Task<Message> { let config = self.connect_config.clone(); @@ -686,7 +703,7 @@ impl AppModel { Ok($res) => $handle, Err(err) => { log::error!("failed '{}': {err}", stringify!($txt)); - cosmic::app::message::none() + return cosmic::app::message::none() } }}; } @@ -703,10 +720,9 @@ impl AppModel { LektordCommand::QueueClear => cmd!(remove_range_from_queue(..)), LektordCommand::QueueCrop => cmd!(remove_range_from_queue(1..)), LektordCommand::QueueGet => cmd!(get_queue_range(..), queue => { - msg!(ChangedQueue(queue.into_iter().fold(<[Vec<KId>; PRIORITY_LENGTH]>::default(), |mut ret, (level, kid)| { - ret[level.index()].push(kid); - ret - }))) + let mut ret = <[Vec<KId>; PRIORITY_LENGTH]>::default(); + queue.into_iter().for_each(|(level, kid)| ret[level.index()].push(kid)); + msg!(ChangedQueue(ret)) }), LektordCommand::QueueLevelShuffle(lvl) => cmd!(shuffle_level_queue(lvl)), LektordCommand::QueueLevelClear(lvl) => cmd!(remove_level_from_queue(lvl)), @@ -735,10 +751,19 @@ impl AppModel { LektordCommand::PlaylistGetContent(id) => cmd!(get_playlist_content(id), content => { msg!(ChangedPlaylistContent(id, content)) }), - LektordCommand::PlaylistsGet => todo!(), - LektordCommand::PlaylistShuffleContent(_) => todo!(), - LektordCommand::PlaylistDelete(_) => todo!(), - LektordCommand::PlaylistRemoveKara(..) => todo!(), + LektordCommand::PlaylistDelete(plt_id) => cmd!(delete_playlist(plt_id)), + LektordCommand::PlaylistRemoveKara(plt_id, kid) => { + cmd!(remove_from_playlist(plt_id, KaraFilter::KId(kid))) + } + LektordCommand::PlaylistsGet => { + cmd!(get_playlists(), playlists => { + let playlists = playlists.into_iter().map(|(id, _)| id).collect(); + msg!(ChangedAvailablePlaylists(playlists)) + }) + } + LektordCommand::PlaylistShuffleContent(id) => cmd!(shuffle_playlist(id), _ => { + cosmic::app::message::app(Message::SendCommand(LektordCommand::PlaylistGetContent(id))) + }), } } } diff --git a/amadeus/src/playlist.rs b/amadeus/src/playlist.rs index dcff1f07..17e1a53b 100644 --- a/amadeus/src/playlist.rs +++ b/amadeus/src/playlist.rs @@ -17,11 +17,15 @@ impl Playlist { self.content.iter().copied() } - pub fn set_content(&mut self, new: Vec<KId>) { - _ = mem::replace(&mut self.content, new); - } - pub fn infos(&self) -> Option<&PlaylistInfo> { self.infos.as_ref() } + + pub fn set_infos(&mut self, infos: PlaylistInfo) { + _ = self.infos.replace(infos); + } + + pub fn set_content(&mut self, new: Vec<KId>) { + _ = mem::replace(&mut self.content, new); + } } diff --git a/amadeus/src/store.rs b/amadeus/src/store.rs index 85a5bd1c..ff6f2297 100644 --- a/amadeus/src/store.rs +++ b/amadeus/src/store.rs @@ -8,7 +8,7 @@ mod queue_level; use crate::playlist::Playlist; use hashbrown::HashMap; -use lektor_payloads::{KId, Kara, Priority, PRIORITY_LENGTH}; +use lektor_payloads::{KId, Kara, PlaylistInfo, Priority, PRIORITY_LENGTH}; use std::mem; /// Stores the kara or its id if the [Kara] struct was not already cached in the [Store]. @@ -67,16 +67,31 @@ impl Store { /// Keep playlists only if their name is specified in the passed [Vec]. Returns the new /// playlists in a vector. - pub fn keep_playlists(&mut self, ids: Vec<KId>) -> Vec<KId> { + pub fn keep_playlists(&mut self, ids: &[KId]) -> Vec<KId> { + log::error!("do better for the code…"); self.playlists.retain(|key, _| ids.contains(key)); - Vec::from_iter(ids.into_iter().flat_map(|id| { + Vec::from_iter(ids.iter().flat_map(|&id| { (!self.playlists.contains_key(&id)).then(|| { - self.playlists.insert(id, Default::default()); + self.playlists.insert(id, Playlist::default()); id }) })) } + /// Set informtions for a playlist. The playlist will be created if needed. If the playlist is + /// created, returns the [KId] of the created playlist. + pub fn set_playlist_infos(&mut self, infos: PlaylistInfo) -> Option<KId> { + let returns = self + .playlists + .contains_key(&infos.local_id()) + .then_some(infos.local_id()); + self.playlists + .entry(infos.local_id()) + .or_default() + .set_infos(infos); + returns + } + /// Set the metadata informations about a kar in the [Store]. Any previous information is /// overwritten. pub fn set(&mut self, kara: Kara) { diff --git a/lektor_lib/src/requests.rs b/lektor_lib/src/requests.rs index 8db36c8a..47e48c43 100644 --- a/lektor_lib/src/requests.rs +++ b/lektor_lib/src/requests.rs @@ -1,8 +1,8 @@ use crate::ConnectConfig; use anyhow::{bail, Context, Result}; use futures::{ + prelude::*, stream::{self, FuturesUnordered}, - StreamExt, }; use lektor_payloads::*; use lektor_utils::{encode_base64, encode_base64_value}; @@ -210,18 +210,22 @@ pub async fn add_to_playlist( pub async fn remove_from_playlist( config: impl AsRef<ConnectConfig>, - name: Arc<str>, + id: KId, what: KaraFilter, ) -> Result<()> { - request!(config; PATCH(PlaylistUpdateAction::Remove(what)) @ "/playlist/{}", encode_base64(name)?) + request!(config; PATCH(PlaylistUpdateAction::Remove(what)) @ "/playlist/{id}") } pub async fn create_playlist(config: impl AsRef<ConnectConfig>, name: Arc<str>) -> Result<()> { request!(config; PUT @ "/playlist/{}", encode_base64(name)?) } -pub async fn delete_playlist(config: impl AsRef<ConnectConfig>, name: Arc<str>) -> Result<()> { - request!(config; DELETE @ "/playlist/{}", encode_base64(name)?) +pub async fn delete_playlist(config: impl AsRef<ConnectConfig>, id: KId) -> Result<()> { + request!(config; DELETE @ "/playlist/{id}") +} + +pub async fn shuffle_playlist(config: impl AsRef<ConnectConfig>, id: KId) -> Result<()> { + request!(config; PATCH(PlaylistUpdateAction::Shuffle) @ "/playlist/{id}") } // ================================================== // diff --git a/lektor_nkdb/src/playlists/playlist.rs b/lektor_nkdb/src/playlists/playlist.rs index e8ae512d..17dd78e6 100644 --- a/lektor_nkdb/src/playlists/playlist.rs +++ b/lektor_nkdb/src/playlists/playlist.rs @@ -125,28 +125,33 @@ impl Playlist { } /// Add a [crate::Kara] by its [KId] to the playlist. - pub fn push(&mut self, id: KId) { - self.content.push(id) + pub fn push(&mut self, id: KId) -> &mut Self { + self.content.push(id); + self } /// Remove a [crate::Kara] by its [KId] from the playlist. - pub fn remove(&mut self, id: KId) { - self.retain(|other| *other != id) + pub fn remove(&mut self, id: KId) -> &mut Self { + self.retain(|other| *other != id); + self } /// Select which [crate::Kara] are kept in the playlist by runing a callback on its [KId]. - pub fn retain(&mut self, cb: impl FnMut(&KId) -> bool) { - self.content.retain(cb) + pub fn retain(&mut self, cb: impl FnMut(&KId) -> bool) -> &mut Self { + self.content.retain(cb); + self } /// Add a [Vec] of [crate::Kara] by their [KId] to the playlist. - pub fn append(&mut self, ids: &mut Vec<KId>) { - self.content.append(ids) + pub fn append(&mut self, ids: &mut Vec<KId>) -> &mut Self { + self.content.append(ids); + self } /// Shuffle the content of the playlist. - pub fn shuffle(&mut self) { - self.content.shuffle(&mut rand::thread_rng()) + pub fn shuffle(&mut self) -> &mut Self { + self.content.shuffle(&mut rand::thread_rng()); + self } } diff --git a/lektor_payloads/src/action.rs b/lektor_payloads/src/action.rs index d5c871f1..823d668b 100644 --- a/lektor_payloads/src/action.rs +++ b/lektor_payloads/src/action.rs @@ -44,6 +44,9 @@ pub enum PlaylistUpdateAction { /// Delete the playlist. Delete, + /// Shuffle the content of the playlist. + Shuffle, + /// Give the playlist to another user. GiveTo(Box<str>), diff --git a/lektord/src/app/routes.rs b/lektord/src/app/routes.rs index 17e538b8..976bd54a 100644 --- a/lektord/src/app/routes.rs +++ b/lektord/src/app/routes.rs @@ -181,23 +181,28 @@ pub(crate) async fn update_playlist( db.write(id, uin, uia, |plt| _ = plt.set_name(new_name)) .await? } + PlaylistUpdateAction::Shuffle => db.write(id, uin, uia, |plt| _ = plt.shuffle()).await?, PlaylistUpdateAction::Remove(filter) => match filter { - KaraFilter::KId(kid) => db.write(id, uin, uia, |plt| plt.remove(kid)).await?, + KaraFilter::KId(kid) => db.write(id, uin, uia, |plt| _ = plt.remove(kid)).await?, KaraFilter::List(_, kids) => { - db.write(id, uin, uia, move |plt| plt.retain(|id| !kids.contains(id))) - .await? + db.write(id, uin, uia, move |plt| { + _ = plt.retain(|id| !kids.contains(id)) + }) + .await? } KaraFilter::Playlist(_, from) => { let kids = db .read(from, |plt| plt.iter_seq_content().collect::<Vec<_>>()) .await?; - db.write(id, uin, uia, move |plt| plt.retain(|id| !kids.contains(id))) - .await? + db.write(id, uin, uia, move |plt| { + _ = plt.retain(|id| !kids.contains(id)) + }) + .await? } }, PlaylistUpdateAction::Add(filter) => match filter { - KaraFilter::KId(kid) => db.write(id, uin, uia, |plt| plt.push(kid)).await?, + KaraFilter::KId(kid) => db.write(id, uin, uia, |plt| _ = plt.push(kid)).await?, KaraFilter::Playlist(rand, from) => { let mut kids = db .read(from, |plt| plt.iter_seq_content().collect::<Vec<_>>()) @@ -205,14 +210,14 @@ pub(crate) async fn update_playlist( if rand { kids[..].shuffle(&mut thread_rng()); } - db.write(id, uin, uia, move |plt| plt.append(&mut kids)) + db.write(id, uin, uia, move |plt| _ = plt.append(&mut kids)) .await? } KaraFilter::List(rand, mut kids) => { if rand { kids[..].shuffle(&mut thread_rng()); } - db.write(id, uin, uia, move |plt| plt.append(&mut kids)) + db.write(id, uin, uia, move |plt| _ = plt.append(&mut kids)) .await? } }, diff --git a/lkt/src/lib.rs b/lkt/src/lib.rs index 051637ef..45446f9d 100644 --- a/lkt/src/lib.rs +++ b/lkt/src/lib.rs @@ -16,7 +16,7 @@ use lektor_lib::*; use lektor_payloads::*; use std::{borrow::Cow, ops::RangeBounds}; -pub async fn collect_karas(config: &ConnectConfig, ids: Vec<KId>) -> Result<Vec<Kara>> { +async fn collect_karas(config: &ConnectConfig, ids: Vec<KId>) -> Result<Vec<Kara>> { let count = ids.len(); let res: Vec<_> = stream::iter(ids.into_iter()) .then(|id| requests::get_kara_by_kid(config, id)) @@ -29,7 +29,7 @@ pub async fn collect_karas(config: &ConnectConfig, ids: Vec<KId>) -> Result<Vec< .ok_or(anyhow!("got errors, didn't received all the karas' data")) } -pub async fn simple_karas_print(config: &ConnectConfig, ids: Vec<KId>) -> Result<()> { +async fn simple_karas_print(config: &ConnectConfig, ids: Vec<KId>) -> Result<()> { let res = collect_karas(config, ids).await?; let count = res.len().to_string().len(); res.into_iter() @@ -38,6 +38,14 @@ pub async fn simple_karas_print(config: &ConnectConfig, ids: Vec<KId>) -> Result Ok(()) } +async fn get_playlist_id_by_name(config: &ConnectConfig, name: &str) -> Result<KId> { + requests::get_playlists(config) + .await? + .into_iter() + .find_map(|(id, plt_name)| (plt_name.as_str() == name).then_some(id)) + .with_context(|| format!("failed to find playlist with name `{name}`")) +} + pub async fn exec_lkt(config: LktConfig, cmd: SubCommand) -> Result<()> { // Write to apply changes... lektor_utils::config::write_config_async("lkt", config.clone()).await?; @@ -255,7 +263,7 @@ pub async fn exec_lkt(config: LktConfig, cmd: SubCommand) -> Result<()> { // Delete a playlist and all its content. Playlist { destroy: Some(n), .. - } => requests::delete_playlist(config, n.into()).await, + } => requests::delete_playlist(config, get_playlist_id_by_name(config, &n).await?).await, // List all the playlists Playlist { @@ -324,11 +332,11 @@ pub async fn exec_lkt(config: LktConfig, cmd: SubCommand) -> Result<()> { remove: Some(mut args), .. } => { - let name = args.remove(0).into(); + let id = get_playlist_id_by_name(config, &args.remove(0)).await?; let rgx = args.join(" ").parse()?; let ids = requests::search_karas(config, SearchFrom::Database, [rgx]).await?; let remove = KaraFilter::List(true, ids); - requests::remove_from_playlist(config, name, remove).await + requests::remove_from_playlist(config, id, remove).await } // ============= // -- GitLab