interfaces for dealing with audio files + move stuff around
parent
752ce3f16c
commit
a1b3bbb999
|
|
@ -3,6 +3,14 @@ name = "memejoin-rs"
|
||||||
version = "0.2.2-alpha"
|
version = "0.2.2-alpha"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "memejoin-rs"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "memejoin_rs"
|
||||||
|
path = "src/lib/mod.rs"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
||||||
160
src/db/mod.rs
160
src/db/mod.rs
|
|
@ -249,66 +249,66 @@ impl Database {
|
||||||
intros
|
intros
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_all_user_permissions(
|
// pub(crate) fn get_all_user_permissions(
|
||||||
&self,
|
// &self,
|
||||||
guild_id: u64,
|
// guild_id: u64,
|
||||||
) -> Result<Vec<(String, auth::Permissions)>> {
|
// ) -> Result<Vec<(String, auth::Permissions)>> {
|
||||||
let mut query = self.conn.prepare(
|
// let mut query = self.conn.prepare(
|
||||||
"
|
// "
|
||||||
SELECT
|
// SELECT
|
||||||
username,
|
// username,
|
||||||
permissions
|
// permissions
|
||||||
FROM UserPermission
|
// FROM UserPermission
|
||||||
WHERE
|
// WHERE
|
||||||
guild_id = :guild_id
|
// guild_id = :guild_id
|
||||||
",
|
// ",
|
||||||
)?;
|
// )?;
|
||||||
|
//
|
||||||
|
// let permissions = query
|
||||||
|
// .query_map(
|
||||||
|
// &[
|
||||||
|
// // :vomit:
|
||||||
|
// (":guild_id", &guild_id.to_string()),
|
||||||
|
// ],
|
||||||
|
// |row| Ok((row.get(0)?, auth::Permissions(row.get(1)?))),
|
||||||
|
// )?
|
||||||
|
// .collect::<Result<Vec<(String, auth::Permissions)>>>()?;
|
||||||
|
//
|
||||||
|
// Ok(permissions)
|
||||||
|
// }
|
||||||
|
|
||||||
let permissions = query
|
// pub(crate) fn get_user_permissions(
|
||||||
.query_map(
|
// &self,
|
||||||
&[
|
// username: &str,
|
||||||
// :vomit:
|
// guild_id: u64,
|
||||||
(":guild_id", &guild_id.to_string()),
|
// ) -> Result<auth::Permissions> {
|
||||||
],
|
// self.conn.query_row(
|
||||||
|row| Ok((row.get(0)?, auth::Permissions(row.get(1)?))),
|
// "
|
||||||
)?
|
// SELECT
|
||||||
.collect::<Result<Vec<(String, auth::Permissions)>>>()?;
|
// permissions
|
||||||
|
// FROM UserPermission
|
||||||
|
// WHERE
|
||||||
|
// username = ?1
|
||||||
|
// AND guild_id = ?2
|
||||||
|
// ",
|
||||||
|
// [username, &guild_id.to_string()],
|
||||||
|
// |row| Ok(auth::Permissions(row.get(0)?)),
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
Ok(permissions)
|
// pub(crate) fn get_user_app_permissions(&self, username: &str) -> Result<auth::AppPermissions> {
|
||||||
}
|
// self.conn.query_row(
|
||||||
|
// "
|
||||||
pub(crate) fn get_user_permissions(
|
// SELECT
|
||||||
&self,
|
// permissions
|
||||||
username: &str,
|
// FROM UserAppPermission
|
||||||
guild_id: u64,
|
// WHERE
|
||||||
) -> Result<auth::Permissions> {
|
// username = ?1
|
||||||
self.conn.query_row(
|
// ",
|
||||||
"
|
// [username],
|
||||||
SELECT
|
// |row| Ok(auth::AppPermissions(row.get(0)?)),
|
||||||
permissions
|
// )
|
||||||
FROM UserPermission
|
// }
|
||||||
WHERE
|
|
||||||
username = ?1
|
|
||||||
AND guild_id = ?2
|
|
||||||
",
|
|
||||||
[username, &guild_id.to_string()],
|
|
||||||
|row| Ok(auth::Permissions(row.get(0)?)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_user_app_permissions(&self, username: &str) -> Result<auth::AppPermissions> {
|
|
||||||
self.conn.query_row(
|
|
||||||
"
|
|
||||||
SELECT
|
|
||||||
permissions
|
|
||||||
FROM UserAppPermission
|
|
||||||
WHERE
|
|
||||||
username = ?1
|
|
||||||
",
|
|
||||||
[username],
|
|
||||||
|row| Ok(auth::AppPermissions(row.get(0)?)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_guild_channels(&self, guild_id: u64) -> Result<Vec<String>> {
|
pub(crate) fn get_guild_channels(&self, guild_id: u64) -> Result<Vec<String>> {
|
||||||
let mut query = self.conn.prepare(
|
let mut query = self.conn.prepare(
|
||||||
|
|
@ -476,28 +476,29 @@ impl Database {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn insert_user_permission(
|
// pub(crate) fn insert_user_permission(
|
||||||
&self,
|
// &self,
|
||||||
username: &str,
|
// username: &str,
|
||||||
guild_id: u64,
|
// guild_id: u64,
|
||||||
permissions: auth::Permissions,
|
// permissions: auth::Permissions,
|
||||||
) -> Result<()> {
|
// ) -> Result<()> {
|
||||||
let affected = self.conn.execute(
|
// let affected = self.conn.execute(
|
||||||
"
|
// "
|
||||||
INSERT INTO
|
// INSERT INTO
|
||||||
UserPermission (username, guild_id, permissions)
|
// UserPermission (username, guild_id, permissions)
|
||||||
VALUES (?1, ?2, ?3)
|
// VALUES (?1, ?2, ?3)
|
||||||
ON CONFLICT(username, guild_id) DO UPDATE SET permissions = ?3",
|
// ON CONFLICT(username, guild_id) DO UPDATE SET permissions = ?3",
|
||||||
[username, &guild_id.to_string(), &permissions.0.to_string()],
|
// [username, &guild_id.to_string(), &permissions.0.to_string()],
|
||||||
)?;
|
// )?;
|
||||||
|
//
|
||||||
if affected < 1 {
|
// if affected < 1 {
|
||||||
warn!("no rows affected when attempting to insert user permissions");
|
// warn!("no rows affected when attempting to insert user permissions");
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
Ok(())
|
// Ok(())
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
/*
|
||||||
pub(crate) fn insert_user_app_permission(
|
pub(crate) fn insert_user_app_permission(
|
||||||
&self,
|
&self,
|
||||||
username: &str,
|
username: &str,
|
||||||
|
|
@ -518,6 +519,7 @@ impl Database {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
pub fn delete_user_intro(
|
pub fn delete_user_intro(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,16 @@
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DiscordSecret {
|
||||||
|
pub client_id: String,
|
||||||
|
pub client_secret: String,
|
||||||
|
pub bot_token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use enum_iterator::Sequence;
|
use enum_iterator::Sequence;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::routes::Error;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub(crate) struct Discord {
|
pub(crate) struct Discord {
|
||||||
pub(crate) access_token: String,
|
pub(crate) access_token: String,
|
||||||
|
|
@ -14,13 +20,6 @@ pub(crate) struct Discord {
|
||||||
pub(crate) scope: String,
|
pub(crate) scope: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct DiscordSecret {
|
|
||||||
pub(crate) client_id: String,
|
|
||||||
pub(crate) client_secret: String,
|
|
||||||
pub(crate) bot_token: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub(crate) struct User {
|
pub(crate) struct User {
|
||||||
pub(crate) auth: Discord,
|
pub(crate) auth: Discord,
|
||||||
|
|
@ -143,3 +142,4 @@ impl FromStr for Permission {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use chrono::{Duration, Utc};
|
use chrono::{Duration, Utc};
|
||||||
|
|
||||||
use crate::lib::domain::intro_tool::{
|
use crate::domain::intro_tool::{
|
||||||
models::{self, guild::IntroId},
|
models::{self, guild::IntroId},
|
||||||
ports::{IntroToolRepository, IntroToolService},
|
ports::{IntroToolRepository, IntroToolService},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::HashMap, future::Future};
|
use std::{collections::HashMap, future::Future};
|
||||||
|
|
||||||
use crate::lib::domain::intro_tool::models::guild::{ChannelName, IntroId};
|
use crate::domain::intro_tool::models::guild::{ChannelName, IntroId};
|
||||||
|
|
||||||
use super::models::guild::{
|
use super::models::guild::{
|
||||||
AddIntroToGuildError, AddIntroToGuildRequest, AddIntroToUserError, AddIntroToUserRequest,
|
AddIntroToGuildError, AddIntroToGuildRequest, AddIntroToUserError, AddIntroToUserRequest,
|
||||||
|
|
@ -116,3 +116,19 @@ pub trait IntroToolRepository: Send + Sync + Clone + 'static {
|
||||||
req: AddIntroToUserRequest,
|
req: AddIntroToUserRequest,
|
||||||
) -> Result<(), AddIntroToUserError>;
|
) -> Result<(), AddIntroToUserError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait RemoteAudioFetcher: Send + Sync + Clone + 'static {
|
||||||
|
fn fetch_remote_audio(
|
||||||
|
&self,
|
||||||
|
url: &str,
|
||||||
|
name: &str,
|
||||||
|
) -> impl Future<Output = Result<String, anyhow::Error>> + Send;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait LocalAudioFetcher: Send + Sync + Clone + 'static {
|
||||||
|
fn save_local_audio(
|
||||||
|
&self,
|
||||||
|
bytes: &[u8],
|
||||||
|
name: &str,
|
||||||
|
) -> impl Future<Output = Result<String, anyhow::Error>> + Send;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,42 @@
|
||||||
use anyhow::{anyhow, Context};
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::domain::intro_tool::{
|
||||||
lib::domain::intro_tool::{
|
models::guild::{self, GetUserError, GuildId, IntroId, User},
|
||||||
models::guild::{self, GetUserError, GuildId, IntroId, User},
|
ports::{IntroToolRepository, IntroToolService, LocalAudioFetcher, RemoteAudioFetcher},
|
||||||
ports::{IntroToolRepository, IntroToolService},
|
|
||||||
},
|
|
||||||
media,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::models;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Service<R>
|
pub struct Service<R, RA, LA>
|
||||||
where
|
where
|
||||||
R: IntroToolRepository,
|
R: IntroToolRepository,
|
||||||
|
RA: RemoteAudioFetcher,
|
||||||
|
LA: LocalAudioFetcher,
|
||||||
{
|
{
|
||||||
repo: R,
|
repo: R,
|
||||||
|
remote_audio_fetcher: RA,
|
||||||
|
local_audio_fetcher: LA,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> Service<R>
|
impl<R, RA, LA> Service<R, RA, LA>
|
||||||
where
|
where
|
||||||
R: IntroToolRepository,
|
R: IntroToolRepository,
|
||||||
|
RA: RemoteAudioFetcher,
|
||||||
|
LA: LocalAudioFetcher,
|
||||||
{
|
{
|
||||||
pub fn new(repo: R) -> Self {
|
pub fn new(repo: R, remote_audio_fetcher: RA, local_audio_fetcher: LA) -> Self {
|
||||||
Self { repo }
|
Self {
|
||||||
|
repo,
|
||||||
|
remote_audio_fetcher,
|
||||||
|
local_audio_fetcher,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> IntroToolService for Service<R>
|
impl<R, RA, LA> IntroToolService for Service<R, RA, LA>
|
||||||
where
|
where
|
||||||
R: IntroToolRepository,
|
R: IntroToolRepository,
|
||||||
|
RA: RemoteAudioFetcher,
|
||||||
|
LA: LocalAudioFetcher,
|
||||||
{
|
{
|
||||||
async fn needs_setup(&self) -> bool {
|
async fn needs_setup(&self) -> bool {
|
||||||
let Ok(guild_count) = self.repo.get_guild_count().await else {
|
let Ok(guild_count) = self.repo.get_guild_count().await else {
|
||||||
|
|
@ -102,42 +108,14 @@ where
|
||||||
) -> Result<IntroId, guild::AddIntroToGuildError> {
|
) -> Result<IntroId, guild::AddIntroToGuildError> {
|
||||||
let file_name = match &req.data {
|
let file_name = match &req.data {
|
||||||
guild::IntroRequestData::Data(bytes) => {
|
guild::IntroRequestData::Data(bytes) => {
|
||||||
// TODO: put this behind an interface
|
self.local_audio_fetcher
|
||||||
let uuid = Uuid::new_v4().to_string();
|
.save_local_audio(bytes, Uuid::new_v4().to_string().as_str())
|
||||||
let temp_path = format!("./sounds/temp/{uuid}");
|
.await?
|
||||||
let dest_path = format!("./sounds/{uuid}.mp3");
|
|
||||||
|
|
||||||
// Write original file so its ready for codec conversion
|
|
||||||
std::fs::write(&temp_path, bytes).context("failed to write temp file")?;
|
|
||||||
media::normalize(&temp_path, &dest_path)
|
|
||||||
.await
|
|
||||||
.context("failed to normalize file")?;
|
|
||||||
std::fs::remove_file(&temp_path).context("failed to remove temp file")?;
|
|
||||||
|
|
||||||
dest_path
|
|
||||||
}
|
}
|
||||||
guild::IntroRequestData::Url(url) => {
|
guild::IntroRequestData::Url(url) => {
|
||||||
let uuid = Uuid::new_v4().to_string();
|
self.remote_audio_fetcher
|
||||||
let file_name = format!("sounds/{uuid}");
|
.fetch_remote_audio(url, Uuid::new_v4().to_string().as_str())
|
||||||
|
.await?
|
||||||
// TODO: put this behind an interface
|
|
||||||
let child = tokio::process::Command::new("yt-dlp")
|
|
||||||
.arg(url)
|
|
||||||
.args(["-o", &file_name])
|
|
||||||
.args(["-x", "--audio-format", "mp3"])
|
|
||||||
.spawn()
|
|
||||||
.context("failed to spawn yt-dlp process")?
|
|
||||||
.wait()
|
|
||||||
.await
|
|
||||||
.context("yt-dlp process failed")?;
|
|
||||||
|
|
||||||
if !child.success() {
|
|
||||||
return Err(guild::AddIntroToGuildError::Unknown(anyhow!(
|
|
||||||
"yt-dlp terminated unsuccessfully"
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
file_name
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ use tracing::info;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
auth,
|
auth,
|
||||||
lib::domain::intro_tool::{models::guild::User, ports::IntroToolService},
|
domain::intro_tool::{models::guild::User, ports::IntroToolService},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use axum::{
|
||||||
http::{HeaderMap, HeaderValue},
|
http::{HeaderMap, HeaderValue},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::lib::{
|
use crate::{
|
||||||
domain::intro_tool::{
|
domain::intro_tool::{
|
||||||
models::guild::{AddIntroToGuildRequest, GuildId, IntroRequestData, User},
|
models::guild::{AddIntroToGuildRequest, GuildId, IntroRequestData, User},
|
||||||
ports::IntroToolService,
|
ports::IntroToolService,
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,12 @@ use axum::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
htmx::{Build, HtmxBuilder, Tag},
|
domain::intro_tool::{
|
||||||
lib::{
|
models::guild::{ChannelName, GuildRef, Intro, User},
|
||||||
domain::intro_tool::{
|
ports::IntroToolService,
|
||||||
models::guild::{ChannelName, GuildRef, Intro, User},
|
|
||||||
ports::IntroToolService,
|
|
||||||
},
|
|
||||||
inbound::{http::ApiState, response::ErrorAsRedirect},
|
|
||||||
},
|
},
|
||||||
|
htmx::{Build, HtmxBuilder, Tag},
|
||||||
|
inbound::{http::ApiState, response::ErrorAsRedirect},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn home<S: IntroToolService>(
|
pub async fn home<S: IntroToolService>(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use axum::{
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::lib::domain::intro_tool::models::guild::{
|
use crate::domain::intro_tool::models::guild::{
|
||||||
AddIntroToGuildError, GetChannelError, GetGuildError, GetIntroError,
|
AddIntroToGuildError, GetChannelError, GetGuildError, GetIntroError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
pub mod auth;
|
||||||
pub mod domain;
|
pub mod domain;
|
||||||
|
pub mod htmx;
|
||||||
pub mod inbound;
|
pub mod inbound;
|
||||||
pub mod outbound;
|
pub mod outbound;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
use anyhow::{anyhow, Context};
|
||||||
|
|
||||||
|
use crate::domain::intro_tool::ports::LocalAudioFetcher;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Ffmpeg;
|
||||||
|
|
||||||
|
impl LocalAudioFetcher for Ffmpeg {
|
||||||
|
async fn save_local_audio(&self, bytes: &[u8], name: &str) -> Result<String, anyhow::Error> {
|
||||||
|
let temp_path = format!("./sounds/temp/{name}");
|
||||||
|
let dest_path = format!("./sounds/{name}.mp3");
|
||||||
|
|
||||||
|
// Write original file so its ready for codec conversion
|
||||||
|
std::fs::write(&temp_path, bytes).context("failed to write temp file")?;
|
||||||
|
let child = tokio::process::Command::new("ffmpeg")
|
||||||
|
.args(["-i", &temp_path])
|
||||||
|
.arg("-vn")
|
||||||
|
.args(["-map", "0:a"])
|
||||||
|
.arg(&dest_path)
|
||||||
|
.spawn()
|
||||||
|
.map_err(|err| anyhow!(err.to_string()))?
|
||||||
|
.wait()
|
||||||
|
.await
|
||||||
|
.map_err(|err| anyhow!(err.to_string()))?;
|
||||||
|
|
||||||
|
if !child.success() {
|
||||||
|
return Err(anyhow!("ffmpeg terminated unsuccessfully"));
|
||||||
|
}
|
||||||
|
std::fs::remove_file(&temp_path).context("failed to remove temp file")?;
|
||||||
|
|
||||||
|
Ok(dest_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1 +1,3 @@
|
||||||
|
pub mod ffmpeg;
|
||||||
pub mod sqlite;
|
pub mod sqlite;
|
||||||
|
pub mod ytdlp;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use tokio::sync::Mutex;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
|
|
||||||
use crate::lib::domain::intro_tool::{
|
use crate::domain::intro_tool::{
|
||||||
models::guild::{
|
models::guild::{
|
||||||
self, AddIntroToGuildError, AddIntroToGuildRequest, AddIntroToUserRequest, Channel,
|
self, AddIntroToGuildError, AddIntroToGuildRequest, AddIntroToUserRequest, Channel,
|
||||||
ChannelName, CreateChannelError, CreateChannelRequest, CreateGuildError,
|
ChannelName, CreateChannelError, CreateChannelRequest, CreateGuildError,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
use anyhow::{anyhow, Context};
|
||||||
|
|
||||||
|
use crate::domain::intro_tool::ports::RemoteAudioFetcher;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Ytdlp;
|
||||||
|
|
||||||
|
impl RemoteAudioFetcher for Ytdlp {
|
||||||
|
async fn fetch_remote_audio(&self, url: &str, name: &str) -> Result<String, anyhow::Error> {
|
||||||
|
let file_name = format!("sounds/{name}");
|
||||||
|
|
||||||
|
let child = tokio::process::Command::new("yt-dlp")
|
||||||
|
.arg(url)
|
||||||
|
.args(["-o", &file_name])
|
||||||
|
.args(["-x", "--audio-format", "mp3"])
|
||||||
|
.spawn()
|
||||||
|
.context("failed to spawn yt-dlp process")?
|
||||||
|
.wait()
|
||||||
|
.await
|
||||||
|
.context("yt-dlp process failed")?;
|
||||||
|
|
||||||
|
if !child.success() {
|
||||||
|
return Err(anyhow!("yt-dlp terminated unsuccessfully"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(format!("{file_name}.mp3"))
|
||||||
|
}
|
||||||
|
}
|
||||||
94
src/main.rs
94
src/main.rs
|
|
@ -1,26 +1,9 @@
|
||||||
// #![feature(stmt_expr_attributes)]
|
|
||||||
// #![feature(proc_macro_hygiene)]
|
|
||||||
// #![feature(async_closure)]
|
|
||||||
|
|
||||||
mod lib;
|
|
||||||
|
|
||||||
mod auth;
|
|
||||||
mod db;
|
mod db;
|
||||||
mod htmx;
|
|
||||||
mod media;
|
|
||||||
mod page;
|
|
||||||
mod routes;
|
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|
||||||
use axum::http::Method;
|
|
||||||
use axum::routing::{get, post};
|
|
||||||
use axum::Router;
|
|
||||||
use settings::ApiState;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tower_http::cors::{Any, CorsLayer};
|
|
||||||
|
|
||||||
use serenity::async_trait;
|
use serenity::async_trait;
|
||||||
use serenity::model::prelude::{Channel, ChannelId, GuildId, Member, Ready};
|
use serenity::model::prelude::{Channel, ChannelId, GuildId, Member, Ready};
|
||||||
|
|
@ -30,9 +13,7 @@ use serenity::prelude::*;
|
||||||
use songbird::SerenityInit;
|
use songbird::SerenityInit;
|
||||||
use tracing::*;
|
use tracing::*;
|
||||||
|
|
||||||
use crate::lib::domain::intro_tool;
|
use memejoin_rs::{auth, domain::intro_tool, inbound, outbound};
|
||||||
use crate::lib::{inbound, outbound};
|
|
||||||
use crate::settings::Settings;
|
|
||||||
|
|
||||||
enum HandlerMessage {
|
enum HandlerMessage {
|
||||||
Ready(Context),
|
Ready(Context),
|
||||||
|
|
@ -120,67 +101,6 @@ impl EventHandler for Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_api(db: Arc<tokio::sync::Mutex<db::Database>>) {
|
|
||||||
let secrets = auth::DiscordSecret {
|
|
||||||
client_id: env::var("DISCORD_CLIENT_ID").expect("expected DISCORD_CLIENT_ID env var"),
|
|
||||||
client_secret: env::var("DISCORD_CLIENT_SECRET")
|
|
||||||
.expect("expected DISCORD_CLIENT_SECRET env var"),
|
|
||||||
bot_token: env::var("DISCORD_TOKEN").expect("expected DISCORD_TOKEN env var"),
|
|
||||||
};
|
|
||||||
let origin = env::var("APP_ORIGIN").expect("expected APP_ORIGIN");
|
|
||||||
|
|
||||||
let state = ApiState {
|
|
||||||
db,
|
|
||||||
secrets,
|
|
||||||
origin: origin.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
tokio::spawn(async move {
|
|
||||||
let api = Router::new()
|
|
||||||
.route("/", get(page::home))
|
|
||||||
.route("/index.html", get(page::home))
|
|
||||||
.route("/login", get(page::login))
|
|
||||||
.route("/guild/:guild_id", get(page::guild_dashboard))
|
|
||||||
.route("/guild/:guild_id/setup", get(routes::guild_setup))
|
|
||||||
.route(
|
|
||||||
"/guild/:guild_id/add_channel",
|
|
||||||
post(routes::guild_add_channel),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/guild/:guild_id/permissions/update",
|
|
||||||
post(routes::update_guild_permissions),
|
|
||||||
)
|
|
||||||
.route("/v2/auth", get(routes::v2_auth))
|
|
||||||
.route(
|
|
||||||
"/v2/intros/add/:guild_id/:channel",
|
|
||||||
post(routes::v2_add_intro_to_user),
|
|
||||||
)
|
|
||||||
.route(
|
|
||||||
"/v2/intros/remove/:guild_id/:channel",
|
|
||||||
post(routes::v2_remove_intro_from_user),
|
|
||||||
)
|
|
||||||
.route("/v2/intros/:guild/add", get(routes::v2_add_guild_intro))
|
|
||||||
.route(
|
|
||||||
"/v2/intros/:guild/upload",
|
|
||||||
post(routes::v2_upload_guild_intro),
|
|
||||||
)
|
|
||||||
.route("/health", get(routes::health))
|
|
||||||
.layer(
|
|
||||||
CorsLayer::new()
|
|
||||||
.allow_origin([origin.parse().unwrap()])
|
|
||||||
.allow_headers(Any)
|
|
||||||
.allow_methods([Method::GET, Method::POST, Method::DELETE]),
|
|
||||||
)
|
|
||||||
.with_state(state);
|
|
||||||
let addr = SocketAddr::from(([0, 0, 0, 0], 8100));
|
|
||||||
info!("socket listening on {addr}");
|
|
||||||
axum::Server::bind(&addr)
|
|
||||||
.serve(api.into_make_service())
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn spawn_bot(db: Arc<tokio::sync::Mutex<db::Database>>) {
|
async fn spawn_bot(db: Arc<tokio::sync::Mutex<db::Database>>) {
|
||||||
let token = env::var("DISCORD_TOKEN").expect("expected DISCORD_TOKEN env var");
|
let token = env::var("DISCORD_TOKEN").expect("expected DISCORD_TOKEN env var");
|
||||||
let songbird = songbird::Songbird::serenity();
|
let songbird = songbird::Songbird::serenity();
|
||||||
|
|
@ -318,10 +238,6 @@ async fn main() -> std::io::Result<()> {
|
||||||
dotenv::dotenv().ok();
|
dotenv::dotenv().ok();
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let settings = serde_json::from_str::<Settings>(
|
|
||||||
&std::fs::read_to_string("config/settings.json").expect("no config/settings.json"),
|
|
||||||
)
|
|
||||||
.expect("error parsing settings file");
|
|
||||||
let secrets = auth::DiscordSecret {
|
let secrets = auth::DiscordSecret {
|
||||||
client_id: env::var("DISCORD_CLIENT_ID").expect("expected DISCORD_CLIENT_ID env var"),
|
client_id: env::var("DISCORD_CLIENT_ID").expect("expected DISCORD_CLIENT_ID env var"),
|
||||||
client_secret: env::var("DISCORD_CLIENT_SECRET")
|
client_secret: env::var("DISCORD_CLIENT_SECRET")
|
||||||
|
|
@ -331,9 +247,12 @@ async fn main() -> std::io::Result<()> {
|
||||||
let origin = env::var("APP_ORIGIN").expect("expected APP_ORIGIN");
|
let origin = env::var("APP_ORIGIN").expect("expected APP_ORIGIN");
|
||||||
|
|
||||||
let db = outbound::sqlite::Sqlite::new("./config/db.sqlite").expect("couldn't open sqlite db");
|
let db = outbound::sqlite::Sqlite::new("./config/db.sqlite").expect("couldn't open sqlite db");
|
||||||
|
let local_audio_fetcher = outbound::ffmpeg::Ffmpeg;
|
||||||
|
let remote_audio_fetcher = outbound::ytdlp::Ytdlp;
|
||||||
|
|
||||||
if let Ok(impersonated_username) = env::var("IMPERSONATED_USERNAME") {
|
if let Ok(impersonated_username) = env::var("IMPERSONATED_USERNAME") {
|
||||||
let service = intro_tool::service::Service::new(db);
|
let service =
|
||||||
|
intro_tool::service::Service::new(db, remote_audio_fetcher, local_audio_fetcher);
|
||||||
let service = intro_tool::debug_service::DebugService::new(service, impersonated_username);
|
let service = intro_tool::debug_service::DebugService::new(service, impersonated_username);
|
||||||
|
|
||||||
let http_server = inbound::http::HttpServer::new(service, secrets, origin)
|
let http_server = inbound::http::HttpServer::new(service, secrets, origin)
|
||||||
|
|
@ -341,7 +260,8 @@ async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
http_server.run().await;
|
http_server.run().await;
|
||||||
} else {
|
} else {
|
||||||
let service = intro_tool::service::Service::new(db);
|
let service =
|
||||||
|
intro_tool::service::Service::new(db, remote_audio_fetcher, local_audio_fetcher);
|
||||||
|
|
||||||
let http_server = inbound::http::HttpServer::new(service, secrets, origin)
|
let http_server = inbound::http::HttpServer::new(service, secrets, origin)
|
||||||
.expect("couldn't start http server");
|
.expect("couldn't start http server");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue