add queue
This commit is contained in:
@@ -7,7 +7,7 @@ license = "MIT"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
symphonia = { version = "0.5.2", features=["all"] }
|
symphonia = { version = "0.5.2", features=["all"] }
|
||||||
songbird = { version = "0.4.0", features = ["driver", "gateway", "twilight", "rustls"] }
|
songbird = { version = "0.4.0", features = ["driver", "gateway", "twilight", "rustls", "builtin-queue"] }
|
||||||
tokio = { features = ["macros", "rt-multi-thread", "signal", "sync"], version = "1" }
|
tokio = { features = ["macros", "rt-multi-thread", "signal", "sync"], version = "1" }
|
||||||
regex = { version = "1", features = ["unicode-case"] }
|
regex = { version = "1", features = ["unicode-case"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
|||||||
196
src/main.rs
196
src/main.rs
@@ -3,11 +3,11 @@ use futures::StreamExt;
|
|||||||
use songbird::{
|
use songbird::{
|
||||||
input::{Compose, YoutubeDl},
|
input::{Compose, YoutubeDl},
|
||||||
shards::TwilightMap,
|
shards::TwilightMap,
|
||||||
tracks::{PlayMode, TrackHandle},
|
|
||||||
Songbird,
|
Songbird,
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, env, error::Error, future::Future, num::NonZeroU64, sync::Arc};
|
use std::{
|
||||||
use tokio::{process::Command, sync::RwLock};
|
env, error::Error, future::Future, num::NonZeroU64, ops::Sub, sync::Arc, time::Duration,
|
||||||
|
};
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
use twilight_cache_inmemory::InMemoryCache;
|
use twilight_cache_inmemory::InMemoryCache;
|
||||||
use twilight_gateway::{
|
use twilight_gateway::{
|
||||||
@@ -15,11 +15,7 @@ use twilight_gateway::{
|
|||||||
Event, Intents, Shard,
|
Event, Intents, Shard,
|
||||||
};
|
};
|
||||||
use twilight_http::Client as HttpClient;
|
use twilight_http::Client as HttpClient;
|
||||||
use twilight_model::{
|
use twilight_model::channel::Message;
|
||||||
channel::Message,
|
|
||||||
gateway::payload::incoming::MessageCreate,
|
|
||||||
id::{marker::GuildMarker, Id},
|
|
||||||
};
|
|
||||||
use twilight_standby::Standby;
|
use twilight_standby::Standby;
|
||||||
|
|
||||||
type State = Arc<StateRef>;
|
type State = Arc<StateRef>;
|
||||||
@@ -28,7 +24,6 @@ type State = Arc<StateRef>;
|
|||||||
struct StateRef {
|
struct StateRef {
|
||||||
http: HttpClient,
|
http: HttpClient,
|
||||||
cache: InMemoryCache,
|
cache: InMemoryCache,
|
||||||
trackdata: RwLock<HashMap<Id<GuildMarker>, TrackHandle>>,
|
|
||||||
songbird: Songbird,
|
songbird: Songbird,
|
||||||
standby: Standby,
|
standby: Standby,
|
||||||
}
|
}
|
||||||
@@ -83,7 +78,6 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
|||||||
Arc::new(StateRef {
|
Arc::new(StateRef {
|
||||||
http,
|
http,
|
||||||
cache,
|
cache,
|
||||||
trackdata: Default::default(),
|
|
||||||
songbird,
|
songbird,
|
||||||
standby: Standby::new(),
|
standby: Standby::new(),
|
||||||
}),
|
}),
|
||||||
@@ -120,11 +114,8 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
|||||||
match msg.content.split(' ').next() {
|
match msg.content.split(' ').next() {
|
||||||
Some("!join") => spawn(join(msg.0, Arc::clone(&state))),
|
Some("!join") => spawn(join(msg.0, Arc::clone(&state))),
|
||||||
Some("!leave") => spawn(leave(msg.0, Arc::clone(&state))),
|
Some("!leave") => spawn(leave(msg.0, Arc::clone(&state))),
|
||||||
Some("!pause") => spawn(pause(msg.0, Arc::clone(&state))),
|
|
||||||
Some("!play") => spawn(play(msg.0, Arc::clone(&state))),
|
Some("!play") => spawn(play(msg.0, Arc::clone(&state))),
|
||||||
Some("!seek") => spawn(seek(msg.0, Arc::clone(&state))),
|
|
||||||
Some("!stop") => spawn(stop(msg.0, Arc::clone(&state))),
|
Some("!stop") => spawn(stop(msg.0, Arc::clone(&state))),
|
||||||
Some("!volume") => spawn(volume(msg.0, Arc::clone(&state))),
|
|
||||||
_ => continue,
|
_ => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,7 +176,6 @@ async fn play(msg: Message, state: State) -> Result<(), Box<dyn Error + Send + S
|
|||||||
msg.author.name
|
msg.author.name
|
||||||
);
|
);
|
||||||
|
|
||||||
let author_id = msg.author.id;
|
|
||||||
let guild_id = msg.guild_id.unwrap();
|
let guild_id = msg.guild_id.unwrap();
|
||||||
|
|
||||||
let url = msg.content.replace("!play", "").trim().to_string();
|
let url = msg.content.replace("!play", "").trim().to_string();
|
||||||
@@ -207,10 +197,16 @@ async fn play(msg: Message, state: State) -> Result<(), Box<dyn Error + Send + S
|
|||||||
|
|
||||||
if let Some(call_lock) = state.songbird.get(guild_id) {
|
if let Some(call_lock) = state.songbird.get(guild_id) {
|
||||||
let mut call = call_lock.lock().await;
|
let mut call = call_lock.lock().await;
|
||||||
let handle = call.play_input(src.into());
|
let _handle = call.enqueue_with_preload(
|
||||||
|
src.into(),
|
||||||
let mut store = state.trackdata.write().await;
|
metadata.duration.map(|duration| -> Duration {
|
||||||
store.insert(guild_id, handle);
|
if duration.as_secs() > 5 {
|
||||||
|
duration.sub(Duration::from_secs(5))
|
||||||
|
} else {
|
||||||
|
duration
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state
|
state
|
||||||
@@ -219,118 +215,6 @@ async fn play(msg: Message, state: State) -> Result<(), Box<dyn Error + Send + S
|
|||||||
.content("Didn't find any results")?
|
.content("Didn't find any results")?
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
// let ytdl = "yt-dlp";
|
|
||||||
// let ytdl_args = [
|
|
||||||
// "-j", // print JSON information for video for metadata
|
|
||||||
// "-q", // don't print progress logs (this messes with -o -)
|
|
||||||
// "--no-simulate", // ensure video is downloaded regardless of printing
|
|
||||||
// "-f",
|
|
||||||
// "webm[abr>0]/bestaudio/best", // select best quality audio-only
|
|
||||||
// "-R",
|
|
||||||
// "infinite", // infinite number of download retries
|
|
||||||
// "--no-playlist", // only download the video if URL also has playlist info
|
|
||||||
// "--ignore-config", // disable all configuration files for a yt-dlp run
|
|
||||||
// "--no-warnings", // don't print out warnings
|
|
||||||
// &msg.content, // URL to download
|
|
||||||
// "-o",
|
|
||||||
// "-", // stream data to stdout
|
|
||||||
// ];
|
|
||||||
// let output = Command::new("yt-dlp")
|
|
||||||
// .args(ytdl_args)
|
|
||||||
// .output()
|
|
||||||
// .await?;
|
|
||||||
// let stdout = output.stdout;
|
|
||||||
|
|
||||||
// info!("metadata: {:?}", output.stderr);
|
|
||||||
|
|
||||||
// if let Some(call_lock) = state.songbird.get(guild_id) {
|
|
||||||
// let mut call = call_lock.lock().await;
|
|
||||||
// let handle = call.play(stdout.into());
|
|
||||||
|
|
||||||
// let mut store = state.trackdata.write().await;
|
|
||||||
// store.insert(guild_id, handle);
|
|
||||||
// }
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn pause(msg: Message, state: State) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
|
||||||
tracing::debug!(
|
|
||||||
"pause command in channel {} by {}",
|
|
||||||
msg.channel_id,
|
|
||||||
msg.author.name
|
|
||||||
);
|
|
||||||
|
|
||||||
let guild_id = msg.guild_id.unwrap();
|
|
||||||
|
|
||||||
let store = state.trackdata.read().await;
|
|
||||||
|
|
||||||
let content = if let Some(handle) = store.get(&guild_id) {
|
|
||||||
let info = handle.get_info().await?;
|
|
||||||
|
|
||||||
let paused = match info.playing {
|
|
||||||
PlayMode::Play => {
|
|
||||||
let _success = handle.pause();
|
|
||||||
false
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let _success = handle.play();
|
|
||||||
true
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let action = if paused { "Unpaused" } else { "Paused" };
|
|
||||||
|
|
||||||
format!("{} the track", action)
|
|
||||||
} else {
|
|
||||||
format!("No track to (un)pause!")
|
|
||||||
};
|
|
||||||
|
|
||||||
state
|
|
||||||
.http
|
|
||||||
.create_message(msg.channel_id)
|
|
||||||
.content(&content)?
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn seek(msg: Message, state: State) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
|
||||||
tracing::debug!(
|
|
||||||
"seek command in channel {} by {}",
|
|
||||||
msg.channel_id,
|
|
||||||
msg.author.name
|
|
||||||
);
|
|
||||||
state
|
|
||||||
.http
|
|
||||||
.create_message(msg.channel_id)
|
|
||||||
.content("Where in the track do you want to seek to (in seconds)?")?
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let author_id = msg.author.id;
|
|
||||||
let msg = state
|
|
||||||
.standby
|
|
||||||
.wait_for_message(msg.channel_id, move |new_msg: &MessageCreate| {
|
|
||||||
new_msg.author.id == author_id
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
let guild_id = msg.guild_id.unwrap();
|
|
||||||
let position = msg.content.parse::<u64>()?;
|
|
||||||
|
|
||||||
let store = state.trackdata.read().await;
|
|
||||||
|
|
||||||
let content = if let Some(handle) = store.get(&guild_id) {
|
|
||||||
let _success = handle.seek(std::time::Duration::from_secs(position));
|
|
||||||
format!("Seeking to {}s", position)
|
|
||||||
} else {
|
|
||||||
format!("No track to seek over!")
|
|
||||||
};
|
|
||||||
|
|
||||||
state
|
|
||||||
.http
|
|
||||||
.create_message(msg.channel_id)
|
|
||||||
.content(&content)?
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -346,7 +230,7 @@ async fn stop(msg: Message, state: State) -> Result<(), Box<dyn Error + Send + S
|
|||||||
|
|
||||||
if let Some(call_lock) = state.songbird.get(guild_id) {
|
if let Some(call_lock) = state.songbird.get(guild_id) {
|
||||||
let mut call = call_lock.lock().await;
|
let mut call = call_lock.lock().await;
|
||||||
let _ = call.stop();
|
call.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
state
|
||||||
@@ -357,53 +241,3 @@ async fn stop(msg: Message, state: State) -> Result<(), Box<dyn Error + Send + S
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn volume(msg: Message, state: State) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
|
|
||||||
tracing::debug!(
|
|
||||||
"volume command in channel {} by {}",
|
|
||||||
msg.channel_id,
|
|
||||||
msg.author.name
|
|
||||||
);
|
|
||||||
state
|
|
||||||
.http
|
|
||||||
.create_message(msg.channel_id)
|
|
||||||
.content("What's the volume you want to set (0.0-10.0, 1.0 being the default)?")?
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let author_id = msg.author.id;
|
|
||||||
let msg = state
|
|
||||||
.standby
|
|
||||||
.wait_for_message(msg.channel_id, move |new_msg: &MessageCreate| {
|
|
||||||
new_msg.author.id == author_id
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
let guild_id = msg.guild_id.unwrap();
|
|
||||||
let volume = msg.content.parse::<f64>()?;
|
|
||||||
|
|
||||||
if !volume.is_finite() || volume > 10.0 || volume < 0.0 {
|
|
||||||
state
|
|
||||||
.http
|
|
||||||
.create_message(msg.channel_id)
|
|
||||||
.content("Invalid volume!")?
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let store = state.trackdata.read().await;
|
|
||||||
|
|
||||||
let content = if let Some(handle) = store.get(&guild_id) {
|
|
||||||
let _success = handle.set_volume(volume as f32);
|
|
||||||
format!("Set the volume to {}", volume)
|
|
||||||
} else {
|
|
||||||
format!("No track to change volume!")
|
|
||||||
};
|
|
||||||
|
|
||||||
state
|
|
||||||
.http
|
|
||||||
.create_message(msg.channel_id)
|
|
||||||
.content(&content)?
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user