improved logging - added save / load command for playlist
This commit is contained in:
parent
9968a4e74a
commit
361a5df7cd
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,3 @@
|
||||
/target
|
||||
.env
|
||||
/ressources
|
||||
/resources
|
||||
@ -19,6 +19,7 @@ dotenv = "0.15.0"
|
||||
serde = "1.0.219"
|
||||
symphonia = { version = "0.5.4", features = ["mp3"] }
|
||||
once_cell = "1.21.3"
|
||||
tracing = "0.1.41"
|
||||
|
||||
|
||||
[dependencies.songbird]
|
||||
|
||||
@ -15,6 +15,7 @@ async fn ignore_stop_event() -> bool {
|
||||
let mut last = LAST_STOP_EVENT.lock().await;
|
||||
if let Some(last) = *last {
|
||||
if last.elapsed() < EVENT_SILNCER {
|
||||
tracing::warn!("Received Stop event but, ignoring it...");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -85,6 +86,7 @@ impl PlayHandle {
|
||||
}
|
||||
|
||||
pub async fn leave(&mut self) -> Result<(), PlayerError> {
|
||||
tracing::info!("Leaving channel");
|
||||
self.player.leave().await
|
||||
}
|
||||
|
||||
@ -116,7 +118,6 @@ impl PlayHandle {
|
||||
|
||||
pub async fn stop(&mut self) -> Result<(), PlayerError> {
|
||||
ignore_stop_event().await;
|
||||
println!("Stopping player from PlayHandle");
|
||||
self.player.stop().await
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
use rand::seq::SliceRandom;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
play::{
|
||||
@ -215,7 +216,7 @@ impl PlayHandle {
|
||||
self.qs_back(to_enqueue.into_iter().map(Into::into).collect())
|
||||
.await;
|
||||
}
|
||||
println!("Handled Mix mode");
|
||||
tracing::debug!("Handled Jukebox / Mix Mode");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ use tokio::sync::Mutex;
|
||||
|
||||
pub mod handle;
|
||||
pub mod jukebox;
|
||||
pub mod persistence;
|
||||
pub mod playlist;
|
||||
pub struct PlayHandleWrapper(Arc<Mutex<handle::PlayHandle>>);
|
||||
|
||||
@ -36,10 +37,10 @@ impl SongEndHandler {
|
||||
}
|
||||
|
||||
pub async fn on_stop_event(&self) {
|
||||
println!("SongEndHandler: received stop event");
|
||||
tracing::info!("SongEndHandler: received stop event");
|
||||
let mut player = self.player.lock().await;
|
||||
if let Err(e) = player.on_stop_event().await {
|
||||
println!("Error playing next track: {e:?}");
|
||||
tracing::error!("Error playing next track: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
70
src/controller/persistence.rs
Normal file
70
src/controller/persistence.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use core::num;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufReader, BufWriter},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||
|
||||
use crate::play::{
|
||||
QueueElement,
|
||||
queue::{PlaylistModification, QueueStructure},
|
||||
};
|
||||
|
||||
use super::handle::PlayHandle;
|
||||
const PLAYLIST: &str = "playlist.json";
|
||||
|
||||
fn save_default<T: Serialize>(list: &T) -> Result<(), std::io::Error> {
|
||||
save(list, PLAYLIST)
|
||||
}
|
||||
|
||||
fn save<T: Serialize>(list: &T, path: &str) -> Result<(), std::io::Error> {
|
||||
let file = File::create(path)?;
|
||||
let writer = BufWriter::new(file);
|
||||
serde_json::to_writer(writer, list)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load<T: DeserializeOwned>(path: &str) -> Result<T, std::io::Error> {
|
||||
let file = File::open(path)?;
|
||||
let reader = BufReader::new(file);
|
||||
let list = serde_json::from_reader(reader)?;
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
fn load_default<T: DeserializeOwned>() -> Result<T, std::io::Error> {
|
||||
load(PLAYLIST)
|
||||
}
|
||||
|
||||
impl PlayHandle {
|
||||
pub fn save_playlist(&self) -> Result<usize, std::io::Error> {
|
||||
let playlist = self.lists().q();
|
||||
save_default(playlist)?;
|
||||
Ok(playlist.len())
|
||||
}
|
||||
pub fn load_playlist(&mut self) -> Result<usize, std::io::Error> {
|
||||
let playlist: Vec<QueueElement> = load_default()?;
|
||||
let num = playlist.len();
|
||||
self.lists_mut().clear();
|
||||
self.lists_mut().qs_front(playlist);
|
||||
Ok(num)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Test {
|
||||
a: i32,
|
||||
b: String,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f() {
|
||||
let test = Test {
|
||||
a: 1,
|
||||
b: "test".to_string(),
|
||||
};
|
||||
save_default(&test).unwrap();
|
||||
let loaded: Test = load_default().unwrap();
|
||||
assert_eq!(test.a, loaded.a);
|
||||
assert_eq!(test.b, loaded.b);
|
||||
}
|
||||
@ -147,6 +147,7 @@ impl QueueStructure for PlayHandle {
|
||||
}
|
||||
}
|
||||
|
||||
// Misc
|
||||
impl PlayHandle {
|
||||
pub fn q(&self) -> &Queue<QueueElement> {
|
||||
self.lists().get_queue()
|
||||
|
||||
73
src/main.rs
73
src/main.rs
@ -62,6 +62,29 @@ fn str_err(e: impl Display) -> Error {
|
||||
format!("{e}").into()
|
||||
}
|
||||
|
||||
trait ResLog {
|
||||
fn log_mer(self, msg: &str) -> Self;
|
||||
fn log_er(self) -> Self;
|
||||
}
|
||||
|
||||
impl<T, E> ResLog for Result<T, E>
|
||||
where
|
||||
E: Display,
|
||||
{
|
||||
fn log_mer(self, msg: &str) -> Self {
|
||||
if let Err(e) = &self {
|
||||
tracing::error!("Error in {msg}: {e}");
|
||||
}
|
||||
self
|
||||
}
|
||||
fn log_er(self) -> Self {
|
||||
if let Err(e) = &self {
|
||||
tracing::error!("Error: {e}");
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
trait DiscordResultOutput<T> {
|
||||
async fn say_err(self, ctx: &CmdContext<'_>) -> Result<T, Error>;
|
||||
async fn say_msg_or_err<S: Into<String>>(
|
||||
@ -132,19 +155,27 @@ async fn on_error(error: poise::FrameworkError<'_, ContextData, Error>) {
|
||||
match error {
|
||||
poise::FrameworkError::Setup { error, .. } => panic!("Failed to start bot: {error:?}"),
|
||||
poise::FrameworkError::Command { error, ctx, .. } => {
|
||||
println!("Error in command `{}`: {:?}", ctx.command().name, error,);
|
||||
tracing::error!("Error in command `{}`: {:?}", ctx.command().name, error,);
|
||||
}
|
||||
error => {
|
||||
if let Err(e) = poise::builtins::on_error(error).await {
|
||||
println!("Error while handling error: {e}");
|
||||
tracing::error!("Error while handling error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setup() {
|
||||
tracing_subscriber::fmt::init();
|
||||
match dotenv::dotenv() {
|
||||
Ok(_) => tracing::info!("Loaded env vars from .env"),
|
||||
Err(e) => tracing::warn!("Failed to load env vars from .env: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::fmt::init();
|
||||
setup();
|
||||
let options = poise::FrameworkOptions {
|
||||
commands: vec![
|
||||
help(),
|
||||
@ -170,6 +201,8 @@ async fn main() {
|
||||
playing(),
|
||||
list(),
|
||||
list_genre(),
|
||||
save_list(),
|
||||
load_list(),
|
||||
],
|
||||
prefix_options: poise::PrefixFrameworkOptions {
|
||||
prefix: Some("~".into()),
|
||||
@ -210,7 +243,7 @@ async fn main() {
|
||||
skip_checks_for_owners: false,
|
||||
event_handler: |_ctx, event, _framework, _data| {
|
||||
Box::pin(async move {
|
||||
println!(
|
||||
tracing::info!(
|
||||
"Got an event in event handler: {:?}",
|
||||
event.snake_case_name()
|
||||
);
|
||||
@ -223,7 +256,7 @@ async fn main() {
|
||||
let framework = poise::Framework::builder()
|
||||
.setup(move |ctx, _ready, framework| {
|
||||
Box::pin(async move {
|
||||
println!("Logged in as {}", _ready.user.name);
|
||||
tracing::info!("Logged in as {}", _ready.user.name);
|
||||
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||
Ok(ContextData {
|
||||
last_request: Mutex::new(None),
|
||||
@ -233,7 +266,6 @@ async fn main() {
|
||||
})
|
||||
.options(options)
|
||||
.build();
|
||||
dotenv::dotenv().ok();
|
||||
let token = std::env::var("TOKEN")
|
||||
.expect("Missing `DISCORD_TOKEN` env var, see README for more information.");
|
||||
let intents = serenity::GatewayIntents::non_privileged()
|
||||
@ -256,11 +288,14 @@ pub async fn fuckoff(ctx: CmdContext<'_>) -> Result<(), Error> {
|
||||
.await
|
||||
.leave()
|
||||
.await
|
||||
.say_msg_or_err("Bye, bye", &ctx)
|
||||
.say_msg_or_err(
|
||||
"Today is not all days. I will be back, no doubt about it.",
|
||||
&ctx,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// Clears playlist
|
||||
/// Clears playlist
|
||||
#[poise::command(prefix_command, track_edits, slash_command, category = "Player")]
|
||||
pub async fn clear(ctx: CmdContext<'_>) -> Result<(), Error> {
|
||||
let player = ctx.data().handle.inner();
|
||||
@ -309,6 +344,7 @@ pub async fn search2(
|
||||
query: String,
|
||||
) -> Result<(), Error> {
|
||||
let param = parse_cmd_solo_args(&query, &Cmd::Search2.allowed_param());
|
||||
// todo fix type
|
||||
if param.is_empty() {
|
||||
ctx.say("No search parameter found").await?;
|
||||
return Ok(());
|
||||
@ -753,5 +789,26 @@ pub async fn shutdown(ctx: CmdContext<'_>) -> Result<(), Error> {
|
||||
ctx.say("Alright then, bye.").await?;
|
||||
let player = ctx.data().handle.inner();
|
||||
_ = player.lock().await.leave().await;
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
/// Save Playlist to file
|
||||
#[poise::command(prefix_command, slash_command, category = "System")]
|
||||
pub async fn save_list(ctx: CmdContext<'_>) -> Result<(), Error> {
|
||||
let player = ctx.data().handle.inner();
|
||||
let cnt = player.lock().await.save_playlist().log_er()?;
|
||||
ctx.say(format!("Saved {cnt} entries from playlist"))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load Playlist to file
|
||||
#[poise::command(prefix_command, slash_command, category = "System")]
|
||||
pub async fn load_list(ctx: CmdContext<'_>) -> Result<(), Error> {
|
||||
let player = ctx.data().handle.inner();
|
||||
let cnt = player.lock().await.load_playlist().log_er()?;
|
||||
ctx.say(format!("Added {cnt} entries from file to playlist"))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::sonic::response::TrackInfo;
|
||||
|
||||
pub mod cache;
|
||||
@ -6,7 +8,7 @@ pub mod player;
|
||||
pub mod queue;
|
||||
pub mod state;
|
||||
|
||||
#[derive(Debug, Clone, std::hash::Hash, PartialEq, Eq, Default)]
|
||||
#[derive(Debug, Clone, std::hash::Hash, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
pub struct QueueElement {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
|
||||
@ -22,10 +22,9 @@ impl Player {
|
||||
}
|
||||
pub async fn stop(&mut self) -> Result<(), PlayerError> {
|
||||
self.err_if_disconnect()?;
|
||||
println!("Before stopping - state {:?}", self.state);
|
||||
self.set_state(PlayerState::Stopped);
|
||||
if let Some(call) = &self.call_handle {
|
||||
println!("Stopping player");
|
||||
tracing::info!("Stopping player");
|
||||
let mut call = call.lock().await;
|
||||
call.stop();
|
||||
}
|
||||
@ -110,7 +109,6 @@ impl Player {
|
||||
let mut call = call.lock().await;
|
||||
|
||||
if self.state == PlayerState::Playing {
|
||||
println!("Currently playing stopping .... ");
|
||||
call.stop();
|
||||
}
|
||||
|
||||
@ -146,5 +144,5 @@ impl Player {
|
||||
}
|
||||
|
||||
fn state_update_notification(from: PlayerState, to: PlayerState) {
|
||||
println!("Player state change from {:?} to {:?}", from, to);
|
||||
tracing::info!("Player state change from {:?} to {:?}", from, to);
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::err::PlayerError;
|
||||
|
||||
pub trait QueueStructure {
|
||||
@ -7,7 +9,7 @@ pub trait QueueStructure {
|
||||
fn is_empty(&self) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Deserialize, Serialize)]
|
||||
pub struct Queue<T>(VecDeque<T>);
|
||||
|
||||
impl<T> Queue<T> {
|
||||
@ -171,15 +173,18 @@ impl<T> PlaylistModification<T> for SongLists<T> {
|
||||
|
||||
fn qs_front(&mut self, items: Vec<T>) {
|
||||
let mut new_queue = items.into_iter().collect::<VecDeque<T>>();
|
||||
tracing::info!("Adding {} items to front of the playlist", new_queue.len());
|
||||
new_queue.append(&mut self.queue.0);
|
||||
std::mem::swap(&mut self.queue.0, &mut new_queue);
|
||||
}
|
||||
|
||||
fn qs_back(&mut self, items: Vec<T>) {
|
||||
tracing::info!("Adding {} items to back of the playlist", items.len());
|
||||
self.queue.0.extend(items);
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
tracing::info!("Clearing the play- and playedlist");
|
||||
self.queue.0.clear();
|
||||
self.played.0.clear();
|
||||
}
|
||||
|
||||
@ -62,16 +62,13 @@ impl Default for PlayerConfig {
|
||||
|
||||
impl PlayerConfig {
|
||||
pub fn apply(&mut self, vol: VolChange) {
|
||||
match vol {
|
||||
VolChange::Increase => {
|
||||
self.volume = (self.volume + 0.1).min(1.0);
|
||||
}
|
||||
VolChange::Decrease => {
|
||||
self.volume = (self.volume - 0.1).max(0.0);
|
||||
}
|
||||
VolChange::Set(v) => {
|
||||
self.volume = v;
|
||||
}
|
||||
}
|
||||
let new_vol = match vol {
|
||||
VolChange::Increase => (self.volume + 0.1).min(1.0),
|
||||
VolChange::Decrease => (self.volume - 0.1).max(0.0),
|
||||
VolChange::Set(v) => v,
|
||||
};
|
||||
|
||||
tracing::info!("Changinging volume from {} to {}", self.volume, new_vol);
|
||||
self.volume = new_vol;
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ async fn get_all_artist_track_as_q(
|
||||
match c.await {
|
||||
Ok(tracks) => results.extend(tracks),
|
||||
Err(e) => {
|
||||
println!("Error fetching album: {:?}", e);
|
||||
tracing::warn!("Error fetching album: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user