playing works

This commit is contained in:
2026-03-08 19:41:52 +01:00
parent b7a13646ee
commit 7ff7430306
9 changed files with 1034 additions and 91 deletions
Generated
+958 -54
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -39,7 +39,7 @@ CMD ["/app/master"]
FROM alpine:3.23 AS worker FROM alpine:3.23 AS worker
RUN \ RUN \
apk add --no-cache python3 ffmpeg deno apk add --no-cache python3 ffmpeg deno opus
RUN \ RUN \
wget https://github.com/yt-dlp/yt-dlp/releases/download/2026.02.21/yt-dlp -O /usr/local/bin/yt-dlp && \ wget https://github.com/yt-dlp/yt-dlp/releases/download/2026.02.21/yt-dlp -O /usr/local/bin/yt-dlp && \
+2 -2
View File
@@ -7,11 +7,11 @@ edition = "2024"
async-nats = { version = "0.46.0" } async-nats = { version = "0.46.0" }
postcard = { version = "1.1.3", features = ["use-std"] } postcard = { version = "1.1.3", features = ["use-std"] }
serenity = { version = "0.12.5", default-features = false, features = [ serenity = { version = "0.12.5", default-features = false, features = [
"cache",
"client", "client",
"collector",
"gateway", "gateway",
"rustls_backend", "rustls_backend",
"model",
"collector",
] } ] }
tokio = { version = "1.49.0", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.49.0", features = ["macros", "rt-multi-thread"] }
types = { path = "../../libs/types" } types = { path = "../../libs/types" }
+49 -11
View File
@@ -1,3 +1,5 @@
use std::path::PathBuf;
use postcard::{from_bytes, to_stdvec}; use postcard::{from_bytes, to_stdvec};
use serenity::{ use serenity::{
all::{ all::{
@@ -12,7 +14,7 @@ use serenity::{
model::application::{CommandOptionType, ResolvedOption, ResolvedValue}, model::application::{CommandOptionType, ResolvedOption, ResolvedValue},
}; };
use types::{ use types::{
jobs::{DownloadJob, DownloadResponse, JobResponse}, jobs::{DownloadJob, JobResponse, Jobs, PlayJob},
misc::new_uuid_v4, misc::new_uuid_v4,
}; };
@@ -38,37 +40,69 @@ pub async fn run(
) )
.await?; .await?;
let job = DownloadJob { let download_job = DownloadJob {
uuid: new_uuid_v4(), uuid: new_uuid_v4(),
url: value.to_string(), url: value.to_string(),
}; };
println!("job {:?}", job); println!("job {:?}", download_job);
let response = match nats_client let response = match nats_client
.request("corro-dj.play", to_stdvec(&job).unwrap().into()) .request(
"corro-dj.download",
to_stdvec(&download_job).unwrap().into(),
)
.await .await
{ {
Ok(resp) => resp, Ok(resp) => resp,
Err(_why) => return Err(serenity::Error::Other("send error")), Err(_why) => return Err(serenity::Error::Other("send error")),
}; };
let job_response: JobResponse<DownloadResponse> = from_bytes(&response.payload).unwrap(); let job_response: JobResponse<Jobs> = from_bytes(&response.payload).unwrap();
println!("response: {:?}", job_response); println!("response: {:?}", job_response);
let text_response: String; let text_response: String;
if let Some(error) = job_response.error { if let Some(error) = job_response.error {
text_response = error; text_response = error;
} else if let Some(content) = job_response.content { } else if let Some(Jobs::Download(content)) = job_response.content {
text_response = content.path.display().to_string(); text_response = content.path.display().to_string();
} else { } else {
text_response = "unkown".to_string(); text_response = "unkown".to_string();
} }
interaction interaction
.edit_response(ctx, EditInteractionResponse::new().content(text_response)) .edit_response(ctx, EditInteractionResponse::new().content(&text_response))
.await?;
let guild_id = interaction.guild_id.unwrap();
let channel_id = guild_id
.get_user_voice_state(&ctx.http, interaction.user.id)
.await
.unwrap()
.channel_id
.unwrap();
let play_job = PlayJob {
uuid: new_uuid_v4(),
path: PathBuf::from(&text_response),
guild_id: guild_id.into(),
channel_id: channel_id.into(),
};
println!("job {:?}", play_job);
let _ = match nats_client
.request("corro-dj.play", to_stdvec(&play_job).unwrap().into())
.await
{
Ok(resp) => resp,
Err(_why) => return Err(serenity::Error::Other("send error")),
};
interaction
.edit_response(ctx, EditInteractionResponse::new().content("playing..."))
.await?; .await?;
} else { } else {
interaction interaction
@@ -85,10 +119,14 @@ pub async fn run(
} }
pub fn register() -> CreateCommand { pub fn register() -> CreateCommand {
CreateCommand::new("testnats") CreateCommand::new("play")
.description("test nats") .description("Play a song")
.add_option( .add_option(
CreateCommandOption::new(CommandOptionType::String, "str", "random string") CreateCommandOption::new(
CommandOptionType::String,
"song",
"Name or url of the song to play",
)
.required(false), .required(false),
) )
} }
+2 -2
View File
@@ -24,7 +24,7 @@ impl EventHandler for Handler {
println!("Received command interaction: {command:#?}"); println!("Received command interaction: {command:#?}");
let content = match command.data.name.as_str() { let content = match command.data.name.as_str() {
"testnats" => { "play" => {
commands::testnats::run(&ctx, &command, &self.nats_client) commands::testnats::run(&ctx, &command, &self.nats_client)
.await .await
.unwrap(); .unwrap();
@@ -78,7 +78,7 @@ async fn main() {
let handler = Handler { nats_client }; let handler = Handler { nats_client };
// Build our client. // Build our client.
let mut discord_client = Client::builder(discord_token, GatewayIntents::empty()) let mut discord_client = Client::builder(discord_token, GatewayIntents::non_privileged())
.event_handler(handler) .event_handler(handler)
.await .await
.expect("Error creating discord client"); .expect("Error creating discord client");
+5 -3
View File
@@ -9,12 +9,14 @@ futures = { version = "0.3.32" }
futures-executor = { version = "0.3.32" } futures-executor = { version = "0.3.32" }
postcard = { version = "1.1.3", features = ["use-std"] } postcard = { version = "1.1.3", features = ["use-std"] }
serenity = { version = "0.12.5", default-features = false, features = [ serenity = { version = "0.12.5", default-features = false, features = [
"client", "cache",
"standard_framework", "rustls_backend",
"voice", "voice",
] } ] }
songbird = { version = "0.5.0" } songbird = { git = "https://github.com/beerpsi-forks/songbird.git", branch = "davey" }
symphonia = { version = "0.5.5" }
tokio = { version = "1.49.0", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.49.0", features = ["macros", "rt-multi-thread"] }
types = { path = "../../libs/types" } types = { path = "../../libs/types" }
which = { version = "8.0.0" } which = { version = "8.0.0" }
yt-dlp = { version = "2.4.0" } yt-dlp = { version = "2.4.0" }
rustls = { version = "0.23.37", default-features = false, features = ["ring"] }
+8 -9
View File
@@ -1,6 +1,6 @@
mod workers; mod workers;
use std::{env, num::NonZeroU64, path::PathBuf, str::FromStr}; use std::env;
use futures::StreamExt; use futures::StreamExt;
use postcard::{from_bytes, to_stdvec}; use postcard::{from_bytes, to_stdvec};
@@ -10,7 +10,7 @@ use serenity::{
async_trait, async_trait,
}; };
use songbird::SerenityInit; use songbird::SerenityInit;
use types::{jobs::{JobResponse, Jobs, PlayJob}, misc::new_uuid_v4}; use types::jobs::{JobResponse, Jobs};
use which::which; use which::which;
use yt_dlp::{Downloader, client::Libraries}; use yt_dlp::{Downloader, client::Libraries};
@@ -49,12 +49,7 @@ impl EventHandler for Handler {
error: res.error, error: res.error,
}), }),
"play" => { "play" => {
workers::play::play(&voice_manager, PlayJob { workers::play::play(&voice_manager, from_bytes(&message.payload).unwrap())
uuid: new_uuid_v4(),
path: PathBuf::new(),
guild_id: NonZeroU64::from_str("302837320250294283").unwrap(),
channel_id: NonZeroU64::from_str("863014198703423499").unwrap()
})
.await .await
.map(|res| JobResponse { .map(|res| JobResponse {
content: res.content.map(Jobs::Play), content: res.content.map(Jobs::Play),
@@ -86,6 +81,10 @@ impl EventHandler for Handler {
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
let discord_token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment"); let discord_token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
let nats_client = async_nats::connect("nats://localhost:4222") let nats_client = async_nats::connect("nats://localhost:4222")
@@ -105,7 +104,7 @@ async fn main() {
yt_downloader, yt_downloader,
}; };
let mut discord_client = Client::builder(&discord_token, GatewayIntents::empty()) let mut discord_client = Client::builder(&discord_token, GatewayIntents::non_privileged())
.event_handler(handler) .event_handler(handler)
.register_songbird() .register_songbird()
.await .await
+1 -1
View File
@@ -15,7 +15,7 @@ pub async fn download(
let audio_path = match downloader let audio_path = match downloader
.download_audio_stream_with_quality( .download_audio_stream_with_quality(
&video, &video,
format!("{}.opus", video.id), format!("{}.ogg", video.id),
yt_dlp::model::AudioQuality::Best, yt_dlp::model::AudioQuality::Best,
yt_dlp::model::AudioCodecPreference::Opus, yt_dlp::model::AudioCodecPreference::Opus,
) )
+7 -7
View File
@@ -1,9 +1,11 @@
use std::sync::Arc; use std::sync::Arc;
use serenity::async_trait; use serenity::async_trait;
use songbird::Songbird; use songbird::{
use songbird::events::{Event, EventContext, EventHandler as VoiceEventHandler, TrackEvent}; Songbird,
use songbird::input::File; events::{Event, EventContext, EventHandler as VoiceEventHandler, TrackEvent},
input::File,
};
use types::jobs::{JobResponse, PlayJob, PlayResponse}; use types::jobs::{JobResponse, PlayJob, PlayResponse};
pub async fn play( pub async fn play(
@@ -31,18 +33,16 @@ pub async fn play(
let mut handler = handler_lock.lock().await; let mut handler = handler_lock.lock().await;
let src = File::new("output/sQaKWttol58.opus").clone(); let src = File::new(job.path).clone();
let track_handle = handler.play_input(src.into()); let track_handle = handler.play_input(src.into());
println!("before info"); println!("before info {:?}", track_handle);
println!("{:?}", track_handle.get_info().await); println!("{:?}", track_handle.get_info().await);
println!("after info"); println!("after info");
let _ = track_handle.play();
Ok(JobResponse { Ok(JobResponse {
content: Some(PlayResponse {}), content: Some(PlayResponse {}),
error: None, error: None,