mirror of
https://github.com/eRgo35/lyra.git
synced 2026-02-04 20:36:10 +01:00
code cleanup and play command polish
This commit is contained in:
3
src/commands.rs
Normal file
3
src/commands.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod kashi;
|
||||
pub mod music;
|
||||
pub mod tools;
|
||||
3
src/commands/kashi.rs
Normal file
3
src/commands/kashi.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod kashi;
|
||||
|
||||
pub use kashi::kashi;
|
||||
@@ -1 +0,0 @@
|
||||
pub mod kashi;
|
||||
24
src/commands/music.rs
Normal file
24
src/commands/music.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
pub mod deafen;
|
||||
pub mod join;
|
||||
pub mod leave;
|
||||
pub mod misc;
|
||||
pub mod mute;
|
||||
pub mod pause;
|
||||
pub mod play;
|
||||
pub mod queue;
|
||||
pub mod repeat;
|
||||
pub mod resume;
|
||||
pub mod skip;
|
||||
pub mod stop;
|
||||
|
||||
pub use deafen::deafen;
|
||||
pub use join::join;
|
||||
pub use leave::leave;
|
||||
pub use mute::mute;
|
||||
pub use pause::pause;
|
||||
pub use play::play;
|
||||
pub use queue::queue;
|
||||
pub use repeat::repeat;
|
||||
pub use resume::resume;
|
||||
pub use skip::skip;
|
||||
pub use stop::stop;
|
||||
@@ -1,12 +0,0 @@
|
||||
pub mod deafen;
|
||||
pub mod join;
|
||||
pub mod leave;
|
||||
pub mod misc;
|
||||
pub mod mute;
|
||||
pub mod play;
|
||||
pub mod queue;
|
||||
pub mod skip;
|
||||
pub mod stop;
|
||||
pub mod repeat;
|
||||
pub mod pause;
|
||||
pub mod resume;
|
||||
@@ -1,14 +1,38 @@
|
||||
use crate::{Context, Error};
|
||||
|
||||
use fancy_regex::Regex;
|
||||
use regex::Regex as Regex_Classic;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
use poise::CreateReply;
|
||||
use poise::serenity_prelude::CreateEmbed;
|
||||
use poise::serenity_prelude::Colour;
|
||||
use poise::serenity_prelude::model::Timestamp;
|
||||
use serenity::builder::CreateEmbedAuthor;
|
||||
use serenity::builder::CreateEmbedFooter;
|
||||
use songbird::input::AuxMetadata;
|
||||
use songbird::input::{Compose, YoutubeDl};
|
||||
use songbird::events::TrackEvent;
|
||||
|
||||
use crate::commands::music::misc::TrackErrorNotifier;
|
||||
use crate::http::HttpKey;
|
||||
|
||||
#[poise::command(prefix_command, slash_command)]
|
||||
pub async fn play(ctx: Context<'_>, url: String) -> Result<(), Error> {
|
||||
let is_search = !url.starts_with("http");
|
||||
#[poise::command(
|
||||
prefix_command,
|
||||
slash_command,
|
||||
aliases("p", "enqueue")
|
||||
)]
|
||||
pub async fn play(
|
||||
ctx: Context<'_>,
|
||||
#[description = "Provide a query or an url"] #[rest] mut song: String,
|
||||
) -> Result<(), Error> {
|
||||
let regex_spotify = Regex::new(r"https?:\/\/(?:embed\.|open\.)(?:spotify\.com\/)(?:track\/|\?uri=spotify:track:)((\w|-)+)(?:(?=\?)(?:[?&]foo=(\d*)(?=[&#]|$)|(?![?&]foo=)[^#])+)?(?=#|$)").unwrap();
|
||||
let regex_youtube = Regex_Classic::new(r#""url": "(https://www.youtube.com/watch\?v=[A-Za-z0-9]{11})""#).unwrap();
|
||||
let regex_youtube_playlist = Regex::new(r"^((?:https?:)\/\/)?((?:www|m)\.)?((?:youtube\.com)).*(youtu.be\/|list=)([^#&?]*).*").unwrap();
|
||||
|
||||
let is_playlist = regex_youtube_playlist.is_match(&song).unwrap();
|
||||
let is_spotify = regex_spotify.is_match(&song).unwrap();
|
||||
let is_query = !song.starts_with("http");
|
||||
|
||||
let guild_id = ctx.guild_id().unwrap();
|
||||
let channel_id = ctx.guild().unwrap().voice_states.get(&ctx.author().id).and_then(|voice_state| voice_state.channel_id);
|
||||
@@ -37,24 +61,100 @@ pub async fn play(ctx: Context<'_>, url: String) -> Result<(), Error> {
|
||||
if let Ok(handler_lock) = manager.join(guild_id, connect_to).await {
|
||||
let mut handler = handler_lock.lock().await;
|
||||
|
||||
// if let Err(err) = handler.deafen(true).await {println!("Failed to deafen: {:?}", err)};
|
||||
handler.add_global_event(TrackEvent::Error.into(), TrackErrorNotifier);
|
||||
|
||||
let mut src = if is_search {
|
||||
println!("ytsearch:{}", url);
|
||||
YoutubeDl::new_ytdl_like("yt-dlp", http_client, format!("ytsearch:{}", url))
|
||||
if is_playlist {
|
||||
let raw_list = Command::new("yt-dlp")
|
||||
.args(["-j", "--flat-playlist", &song])
|
||||
.output()
|
||||
.expect("failed to execute process")
|
||||
.stdout;
|
||||
|
||||
let list = String::from_utf8(raw_list.clone()).expect("Invalid UTF-8");
|
||||
|
||||
let urls: Vec<String> = regex_youtube.captures_iter(&list).map(|capture| capture[1].to_string()).collect();
|
||||
|
||||
let mut sources: Vec<YoutubeDl> = vec![];
|
||||
|
||||
for url in urls {
|
||||
let src = YoutubeDl::new_ytdl_like("yt-dlp", http_client.clone(), url);
|
||||
let _ = handler.enqueue_input(src.clone().into()).await;
|
||||
sources.push(src);
|
||||
}
|
||||
|
||||
let embed = generate_playlist_embed(ctx, sources).await;
|
||||
let response = CreateReply::default().embed(embed.unwrap());
|
||||
ctx.send(response).await?;
|
||||
} else {
|
||||
YoutubeDl::new_ytdl_like("yt-dlp", http_client, url)
|
||||
};
|
||||
if is_spotify {
|
||||
let exec = format!("node ./src/spotify --url {}", song);
|
||||
let query = Command::new("sh").arg("-c").arg(exec).output().expect("failed to execute process").stdout;
|
||||
let query_str = String::from_utf8(query.clone()).expect("Invalid UTF-8");
|
||||
song = format!("ytsearch:{}", query_str.to_string());
|
||||
|
||||
let _ = handler.enqueue_input(src.clone().into()).await;
|
||||
}
|
||||
|
||||
let _metadata = src.aux_metadata().await.unwrap();
|
||||
if is_query {
|
||||
song = format!("ytsearch:{}", song);
|
||||
}
|
||||
|
||||
// ctx.say(format!("Playing song: {}", metadata.title.unwrap())).await?;
|
||||
} else {
|
||||
ctx.say("Not in a voice channel to play in").await?;
|
||||
let src = YoutubeDl::new_ytdl_like("yt-dlp", http_client, song);
|
||||
let _ = handler.enqueue_input(src.clone().into()).await;
|
||||
|
||||
let embed = generate_embed(ctx, src).await;
|
||||
let response = CreateReply::default().embed(embed.unwrap());
|
||||
ctx.send(response).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn generate_embed(ctx: Context<'_>, src: YoutubeDl) -> Result<CreateEmbed, Error> {
|
||||
let metadata = src.clone().aux_metadata().await.unwrap();
|
||||
let AuxMetadata {title, thumbnail, source_url, artist, duration, ..} = metadata;
|
||||
let timestamp = Timestamp::now();
|
||||
let duration_minutes = duration.unwrap_or(Duration::new(0, 0)).clone().as_secs() / 60;
|
||||
let duration_seconds = duration.unwrap_or(Duration::new(0, 0)).clone().as_secs() % 60;
|
||||
|
||||
let embed = CreateEmbed::default()
|
||||
.author(CreateEmbedAuthor::new("Track enqueued").icon_url(ctx.author().clone().face()))
|
||||
.colour(Colour::from_rgb(255, 58, 97))
|
||||
.title(title.unwrap())
|
||||
.url(source_url.unwrap())
|
||||
.thumbnail(thumbnail.unwrap_or(ctx.cache().current_user().face()))
|
||||
.field("Artist", artist.unwrap_or("Unknown Artist".to_string()), true)
|
||||
.field("Duration", format!("{:02}:{:02}", duration_minutes, duration_seconds), true)
|
||||
.field("DJ", ctx.author().name.clone(), true)
|
||||
.timestamp(timestamp)
|
||||
.footer(CreateEmbedFooter::new(ctx.cache().current_user().name.to_string()).icon_url(ctx.cache().current_user().face()));
|
||||
|
||||
Ok(embed)
|
||||
}
|
||||
|
||||
async fn generate_playlist_embed(ctx: Context<'_>, sources: Vec<YoutubeDl>) -> Result<CreateEmbed, Error> {
|
||||
let src = sources.get(0).unwrap();
|
||||
|
||||
let metadata = src.clone().aux_metadata().await.unwrap();
|
||||
let AuxMetadata {title, thumbnail, source_url, artist, duration, ..} = metadata;
|
||||
let timestamp = Timestamp::now();
|
||||
let duration_minutes = duration.unwrap_or(Duration::new(0, 0)).clone().as_secs() / 60;
|
||||
let duration_seconds = duration.unwrap_or(Duration::new(0, 0)).clone().as_secs() % 60;
|
||||
|
||||
let description = format!("Enqueued tracks: {}", sources.len() - 1);
|
||||
|
||||
let embed = CreateEmbed::default()
|
||||
.author(CreateEmbedAuthor::new("Playlist enqueued").icon_url(ctx.author().clone().face()))
|
||||
.colour(Colour::from_rgb(255, 58, 97))
|
||||
.title(title.unwrap())
|
||||
.url(source_url.unwrap())
|
||||
.thumbnail(thumbnail.unwrap_or(ctx.cache().current_user().face()))
|
||||
.field("Artist", artist.unwrap_or("Unknown Artist".to_string()), true)
|
||||
.field("Duration", format!("{:02}:{:02}", duration_minutes, duration_seconds), true)
|
||||
.field("DJ", ctx.author().name.clone(), true)
|
||||
.description(description)
|
||||
.timestamp(timestamp)
|
||||
.footer(CreateEmbedFooter::new(ctx.cache().current_user().name.to_string()).icon_url(ctx.cache().current_user().face()));
|
||||
|
||||
Ok(embed)
|
||||
}
|
||||
|
||||
5
src/commands/tools.rs
Normal file
5
src/commands/tools.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod ping;
|
||||
pub mod register;
|
||||
|
||||
pub use ping::ping;
|
||||
pub use register::register;
|
||||
@@ -1 +0,0 @@
|
||||
pub mod ping;
|
||||
12
src/commands/tools/register.rs
Normal file
12
src/commands/tools/register.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use crate::{Context, Error};
|
||||
|
||||
#[poise::command(prefix_command, check = "check")]
|
||||
pub async fn register(ctx: Context<'_>) -> Result<(), Error> {
|
||||
poise::builtins::register_application_commands_buttons(ctx).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn check(ctx: Context<'_>) -> Result<bool, Error> {
|
||||
let owner = std::env::var("OWNER_ID").expect("Environment variable `OWNER_ID` not found");
|
||||
Ok(ctx.author().id.to_string() == owner)
|
||||
}
|
||||
Reference in New Issue
Block a user