summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSanto Cariotti <santo@dcariotti.me>2025-01-17 20:41:20 +0100
committerSanto Cariotti <santo@dcariotti.me>2025-01-17 20:41:20 +0100
commita56ecb83c70e57144a4b01153aab21656c97e366 (patch)
tree6951efbfa4e89969452b097acb7531d6162de6a6
parentc6dcfcfe833c28fec288918965fc31cd86199fb1 (diff)
Sound as binary string
-rw-r--r--Dockerfile3
-rw-r--r--README.md2
-rw-r--r--assets/sounds/readme.md1
-rw-r--r--docker-compose.yml1
-rw-r--r--schema/init.sql3
-rw-r--r--src/audio.rs59
-rw-r--r--src/config.rs3
-rw-r--r--src/graphql/query.rs2
-rw-r--r--src/graphql/types/alert.rs98
-rw-r--r--src/graphql/types/notification.rs12
-rw-r--r--src/main.rs3
11 files changed, 75 insertions, 112 deletions
diff --git a/Dockerfile b/Dockerfile
index c78d957..57c5e18 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -17,9 +17,6 @@ RUN apt-get update && apt-get install -y libssl-dev ca-certificates
RUN groupadd -g 999 appuser && \
useradd -r -u 999 -g appuser appuser
-RUN mkdir -p /app/assets
-RUN chown -R appuser:appuser /app/assets
-
USER appuser
COPY --from=builder /app/target/release/cas /app
diff --git a/README.md b/README.md
index 342dad3..f1570cc 100644
--- a/README.md
+++ b/README.md
@@ -32,8 +32,6 @@ Now you set up some env variables:
- `UNREALSPEECH_TOKEN`: used by [Unrealspeech](https://unrealspeech.com) for
text-to-speach API.
-- `AUDIO_PATH`: folder path where to store audios
-
After that you must copy the `schema/init.sql` file into the database.
Now just run the app
diff --git a/assets/sounds/readme.md b/assets/sounds/readme.md
deleted file mode 100644
index e4ffb4a..0000000
--- a/assets/sounds/readme.md
+++ /dev/null
@@ -1 +0,0 @@
-Here is the sounds list
diff --git a/docker-compose.yml b/docker-compose.yml
index 211f18e..9291d91 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -31,7 +31,6 @@ services:
- ALLOWED_HOST=${ALLOWED_HOST}
- EXPO_ACCESS_TOKEN=${EXPO_ACCESS_TOKEN}
- UNREALSPEECH_TOKEN=${UNREALSPEECH_TOKEN}
- - AUDIO_PATH=${AUDIO_PATH}
depends_on:
- postgres
diff --git a/schema/init.sql b/schema/init.sql
index 68f1f92..ebd428b 100644
--- a/schema/init.sql
+++ b/schema/init.sql
@@ -34,6 +34,9 @@ CREATE TABLE alerts(
text1 text NOT NULL,
text2 text NOT NULL,
text3 text NOT NULL,
+ audio1 bytea NOT NULL,
+ audio2 bytea NOT NULL,
+ audio3 bytea NOT NULL,
reached_users INTEGER DEFAULT 0 NOT NULL,
PRIMARY KEY(id),
CONSTRAINT fk_users_id
diff --git a/src/audio.rs b/src/audio.rs
index 07ecdd8..925ccd4 100644
--- a/src/audio.rs
+++ b/src/audio.rs
@@ -1,17 +1,8 @@
use crate::config::CONFIG;
-use axum::{
- extract::Path,
- http::{HeaderMap, HeaderName, HeaderValue, StatusCode},
-};
use reqwest::header::AUTHORIZATION;
-use std::{
- fs::{self, File},
- io::Write,
- path::Path as StdPath,
-};
/// Create a new sound from a text
-pub async fn tts(text: String, filename: String) -> Result<(), String> {
+pub async fn tts(text: &String) -> Result<bytes::Bytes, String> {
let url = "https://api.v7.unrealspeech.com/stream";
let api_key = format!("Bearer {}", CONFIG.unrealspeech_token);
@@ -34,59 +25,13 @@ pub async fn tts(text: String, filename: String) -> Result<(), String> {
.map_err(|e| format!("Error creating new audio: {}", e))?;
if response.status().is_success() {
- let filepath = format!("{}/{}", CONFIG.audio_path, filename);
- if let Some(parent) = StdPath::new(&filepath).parent() {
- fs::create_dir_all(parent)
- .map_err(|e| format!("Failed to create directories: {}", e))?;
- }
-
- let mut file =
- File::create(&filepath).map_err(|e| format!("Failed to create file: {}", e))?;
let content = response
.bytes()
.await
.map_err(|e| format!("Failed to get response bytes: {}", e))?;
- file.write_all(&content)
- .map_err(|e| format!("Failed to write file: {}", e))?;
- Ok(())
+ Ok(content)
} else {
Err(format!("Failed to fetch the audio: {}", response.status()))
}
}
-
-/// Axum endpoint which shows files
-pub async fn show_file(
- Path(id): Path<String>,
-) -> Result<(HeaderMap, Vec<u8>), (StatusCode, String)> {
- let index = id.find('.').unwrap_or(usize::MAX);
- let ext_name = if index != usize::MAX {
- &id[index + 1..]
- } else {
- "xxx"
- };
-
- let mut headers = HeaderMap::new();
- if ["mp3"].contains(&ext_name) {
- headers.insert(
- HeaderName::from_static("content-type"),
- HeaderValue::from_str("audio/mpeg").unwrap(),
- );
- }
-
- let file_name = format!("{}/{}", CONFIG.audio_path, id);
- let file_path = StdPath::new(&file_name);
-
- if !file_path.exists() {
- return Err((StatusCode::NOT_FOUND, "File not found".to_string()));
- }
-
- fs::read(file_path)
- .map(|file_content| (headers, file_content))
- .map_err(|_| {
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- "Failed to read file".to_string(),
- )
- })
-}
diff --git a/src/config.rs b/src/config.rs
index 11c46e8..763b852 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -22,9 +22,6 @@ pub struct Configuration {
/// Token used for text-to-speach API
pub unrealspeech_token: String,
-
- /// Audio folder path
- pub audio_path: String,
}
impl Configuration {
diff --git a/src/graphql/query.rs b/src/graphql/query.rs
index 63ca49c..ccade65 100644
--- a/src/graphql/query.rs
+++ b/src/graphql/query.rs
@@ -95,7 +95,7 @@ impl Query {
/// -H 'content-type: application/json'
/// -d '{"query":"{notifications {
/// id,
- /// alert { id, userId, createdAt, area, areaLevel2, areaLevel3, text1, text2, text3, reachedUsers },
+ /// alert { id, userId, createdAt, area, areaLevel2, areaLevel3, text1, text2, text3, audio1, audio2, audio3, reachedUsers },
/// userId, latitude, longitude, movingActivity, level, seen, createdAt
/// }}"}'
/// ```
diff --git a/src/graphql/types/alert.rs b/src/graphql/types/alert.rs
index 9b71188..1156f3d 100644
--- a/src/graphql/types/alert.rs
+++ b/src/graphql/types/alert.rs
@@ -29,6 +29,9 @@ pub struct Alert {
pub text1: String,
pub text2: String,
pub text3: String,
+ pub audio1: Vec<u8>,
+ pub audio2: Vec<u8>,
+ pub audio3: Vec<u8>,
pub reached_users: i32,
}
@@ -82,6 +85,9 @@ pub mod query {
text1,
text2,
text3,
+ audio1,
+ audio2,
+ audio3,
reached_users
FROM alerts
WHERE id = $1",
@@ -101,6 +107,9 @@ pub mod query {
text1,
text2,
text3,
+ audio1,
+ audio2,
+ audio3,
reached_users
FROM alerts
ORDER BY id DESC
@@ -124,6 +133,9 @@ pub mod query {
text1: row.get("text1"),
text2: row.get("text2"),
text3: row.get("text3"),
+ audio1: row.get("audio1"),
+ audio2: row.get("audio2"),
+ audio3: row.get("audio3"),
reached_users: row.get("reached_users"),
})
.collect();
@@ -190,6 +202,7 @@ pub mod mutations {
ST_AsText(ST_Buffer(area::geography, 1000)) as area_level2,
ST_AsText(ST_Buffer(area::geography, 2000)) as area_level3,
text1, text2, text3,
+ audio1, audio2, audio3,
reached_users
FROM alerts WHERE area = {} AND created_at >= NOW() - INTERVAL '10 MINUTE'",
polygon
@@ -207,6 +220,9 @@ pub mod mutations {
text1: row.get("text1"),
text2: row.get("text2"),
text3: row.get("text3"),
+ audio1: row.get("audio1"),
+ audio2: row.get("audio2"),
+ audio3: row.get("audio3"),
reached_users: row.get("reached_users"),
})
.collect::<Vec<Alert>>()
@@ -215,15 +231,40 @@ pub mod mutations {
return Ok(previous_alert);
}
+ let audio1 = match audio::tts(&input.text1).await {
+ Ok(content) => content,
+ Err(e) => {
+ tracing::error!("Error for `{}`: {}", &input.text1, e);
+ bytes::Bytes::new()
+ }
+ };
+
+ let audio2 = match audio::tts(&input.text2).await {
+ Ok(content) => content,
+ Err(e) => {
+ tracing::error!("Error for `{}`: {}", &input.text2, e);
+ bytes::Bytes::new()
+ }
+ };
+
+ let audio3 = match audio::tts(&input.text3).await {
+ Ok(content) => content,
+ Err(e) => {
+ tracing::error!("Error for `{}`: {}", &input.text3, e);
+ bytes::Bytes::new()
+ }
+ };
+
let insert_query = format!(
- "INSERT INTO alerts (user_id, area, text1, text2, text3)
- VALUES($1, {}, $2, $3, $4)
+ "INSERT INTO alerts (user_id, area, text1, text2, text3, audio1, audio2, audio3)
+ VALUES($1, {}, $2, $3, $4, $5, $6, $7)
RETURNING
id, user_id, extract(epoch from created_at)::double precision as created_at,
ST_AsText(area) as area,
ST_AsText(ST_Buffer(area::geography, 1000)) as area_level2,
ST_AsText(ST_Buffer(area::geography, 2000)) as area_level3,
text1, text2, text3,
+ audio1, audio2, audio3,
reached_users",
polygon
);
@@ -231,7 +272,15 @@ pub mod mutations {
let rows = client
.query(
&insert_query,
- &[&claims.user_id, &input.text1, &input.text2, &input.text3],
+ &[
+ &claims.user_id,
+ &input.text1,
+ &input.text2,
+ &input.text3,
+ &audio1.to_vec(),
+ &audio2.to_vec(),
+ &audio3.to_vec(),
+ ],
)
.await?;
let mut alert = rows
@@ -246,6 +295,9 @@ pub mod mutations {
text1: row.get("text1"),
text2: row.get("text2"),
text3: row.get("text3"),
+ audio1: row.get("audio1"),
+ audio2: row.get("audio2"),
+ audio3: row.get("audio3"),
reached_users: row.get("reached_users"),
})
.collect::<Vec<Alert>>()
@@ -311,7 +363,7 @@ pub mod mutations {
let notification = Notification::insert_db(
client,
alert.id,
- &p,
+ p,
LevelAlert::from_str(level.text).unwrap(),
)
.await?;
@@ -363,44 +415,6 @@ pub mod mutations {
)
.await?;
- if let Err(e) = audio::tts(
- alert.text1.clone(),
- format!("alert-{}-text-1.mp3", alert.id),
- )
- .await
- {
- tracing::error!(
- "Error for `{}`: {}",
- format!("alert-{}-text-1.mp3", alert.id),
- e
- );
- }
-
- if let Err(e) = audio::tts(
- alert.text2.clone(),
- format!("alert-{}-text-2.mp3", alert.id),
- )
- .await
- {
- tracing::error!(
- "Error for `{}`: {}",
- format!("alert-{}-text-2.mp3", alert.id),
- e
- );
- }
- if let Err(e) = audio::tts(
- alert.text3.clone(),
- format!("alert-{}-text-3.mp3", alert.id),
- )
- .await
- {
- tracing::error!(
- "Error for `{}`: {}",
- format!("alert-{}-text-3.mp3", alert.id),
- e
- );
- }
-
Ok(alert)
}
}
diff --git a/src/graphql/types/notification.rs b/src/graphql/types/notification.rs
index 21fc1aa..87d478a 100644
--- a/src/graphql/types/notification.rs
+++ b/src/graphql/types/notification.rs
@@ -186,6 +186,9 @@ pub mod query {
a.text1 as alert_text1,
a.text2 as alert_text2,
a.text3 as alert_text3,
+ a.audio1 as alert_audio1,
+ a.audio2 as alert_audio2,
+ a.audio3 as alert_audio3,
a.reached_users as alert_reached_users
FROM notifications n
JOIN alerts a ON n.alert_id = a.id".to_string();
@@ -242,6 +245,9 @@ pub mod query {
text1: row.get("alert_text1"),
text2: row.get("alert_text2"),
text3: row.get("alert_text3"),
+ audio1: row.get("alert_audio1"),
+ audio2: row.get("alert_audio2"),
+ audio3: row.get("alert_audio3"),
reached_users: row.get("alert_reached_users"),
},
seen: row.get("seen"),
@@ -296,6 +302,9 @@ pub mod mutations {
a.text1 as alert_text1,
a.text2 as alert_text2,
a.text3 as alert_text3,
+ a.audio1 as alert_audio1,
+ a.audio2 as alert_audio2,
+ a.audio3 as alert_audio3,
a.reached_users as alert_reached_users
FROM notifications n
JOIN alerts a ON n.alert_id = a.id
@@ -316,6 +325,9 @@ pub mod mutations {
text1: row.get("alert_text1"),
text2: row.get("alert_text2"),
text3: row.get("alert_text3"),
+ audio1: row.get("alert_audio1"),
+ audio2: row.get("alert_audio2"),
+ audio3: row.get("alert_audio3"),
reached_users: row.get("alert_reached_users"),
},
seen: row.get("seen"),
diff --git a/src/main.rs b/src/main.rs
index bf8afda..e27f887 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -14,7 +14,7 @@ use crate::config::CONFIG;
use async_graphql::{EmptySubscription, Schema};
use axum::{
http::{header, Method, Request},
- routing::{get, post},
+ routing::post,
Extension, Router,
};
use errors::AppError;
@@ -47,7 +47,6 @@ async fn create_app() -> Result<Router, AppError> {
.finish();
Ok(Router::new()
- .route("/assets/sounds/:id", get(audio::show_file))
.route(
"/graphql",
post(graphql::routes::graphql_handler).layer(Extension(schema.clone())),