diff --git a/migrations/20240620151940_init.sql b/migrations/20240620151940_init.sql index 900212a..17cd4cd 100644 --- a/migrations/20240620151940_init.sql +++ b/migrations/20240620151940_init.sql @@ -1,4 +1,3 @@ --- Add migration script here CREATE TABLE IF NOT EXISTS tracks ( id INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/migrations/20240627193022_save_tracks.sql b/migrations/20240627193022_save_tracks.sql new file mode 100644 index 0000000..cea826c --- /dev/null +++ b/migrations/20240627193022_save_tracks.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS blobs +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + data BLOB +); diff --git a/src/commands/play.rs b/src/commands/play.rs index 633de8c..977dac4 100644 --- a/src/commands/play.rs +++ b/src/commands/play.rs @@ -1,14 +1,18 @@ use crate::commands::join::join_channel; +use crate::db::track::{insert_blob, Blob}; use crate::metadata::{Metadata, MetadataMap}; -use crate::state::State; +use crate::state::{State, StateRef}; use crate::{colors, db}; +use async_trait::async_trait; use serde::{Deserialize, Serialize}; use songbird::input::cached::Memory; use songbird::input::{Compose, YoutubeDl}; use songbird::tracks::Track; -use std::io::{BufRead, BufReader}; +use songbird::{Event, EventContext, EventHandler, TrackEvent}; +use std::io::{BufRead, BufReader, Read}; use std::ops::Sub; +use std::sync::Arc; use std::{error::Error, time::Duration}; use tokio::process::Command; use tracing::debug; @@ -183,10 +187,8 @@ pub(crate) async fn play( .ok_or("Could not find url")?; let mut src = YoutubeDl::new(reqwest::Client::new(), url.clone()); - let src_copy = src.clone(); - let src_copy2 = src.clone(); - let _m = Memory::new(src_copy2.into()); - let track: Track = src_copy.into(); + let memory = Memory::new(src.clone().into()).await.unwrap(); + let track: Track = memory.new_handle().into(); if let Ok(metadata) = src.aux_metadata().await { debug!("metadata: {:?}", metadata); @@ -255,6 +257,17 @@ pub(crate) async fn play( url, src, }); + + handle + .add_event( + Event::Track(TrackEvent::Preparing), + TrackPreparingNotifier { + memory, + track_id, + state: Arc::clone(&state), + }, + ) + .expect("could not add event"); } } } @@ -350,6 +363,31 @@ pub(crate) async fn play( Ok(()) } +struct TrackPreparingNotifier { + memory: Memory, + track_id: i64, + state: Arc, +} + +#[async_trait] +impl EventHandler for TrackPreparingNotifier { + async fn act(&self, _ctx: &EventContext<'_>) -> Option { + tracing::info!("Build buffer"); + let mut reader = BufReader::new(self.memory.new_handle()); + let mut bytes = Vec::new(); + reader + .read_to_end(&mut bytes) + .expect("could not read track in memory"); + + tracing::info!("Saving track"); + insert_blob(&self.state.pool, Blob::new(self.track_id, bytes)) + .await + .expect("could not insert blob"); + tracing::info!("Saved"); + None + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/db/track.rs b/src/db/track.rs index b13bd22..d423c29 100644 --- a/src/db/track.rs +++ b/src/db/track.rs @@ -144,3 +144,25 @@ pub(crate) async fn insert_guild( .await?; Ok(res.last_insert_rowid()) } + +#[derive(Debug, FromRow)] +pub(crate) struct Blob { + pub(crate) id: i64, + pub(crate) data: Vec, +} + +impl Blob { + pub(crate) fn new(id: i64, data: Vec) -> Self { + Self { id, data } + } +} + +pub(crate) async fn insert_blob(pool: &sqlx::SqlitePool, blob: Blob) -> Result<(), sqlx::Error> { + let query = "INSERT OR REPLACE INTO blobs (id, data) VALUES ($1, $2)"; + sqlx::query(query) + .bind(blob.id) + .bind(blob.data) + .execute(pool) + .await?; + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 797c333..7116b11 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use futures::StreamExt; use signal::signal_handler; use songbird::{shards::TwilightMap, Songbird}; use state::StateRef; -use std::{env, error::Error, sync::Arc}; +use std::{env, error::Error, str::FromStr, sync::Arc}; use tokio::select; use tracing::{debug, info}; use twilight_cache_inmemory::InMemoryCache; @@ -41,9 +41,9 @@ async fn main() -> Result<(), Box> { let (mut shards, state) = { let db = env::var("DATABASE_URL").map_err(|_| "DATABASE_URL is not set")?; - let options = SqliteConnectOptions::new() - .create_if_missing(true) - .filename(&db); + let options = SqliteConnectOptions::from_str(&db) + .expect("could not create options") + .create_if_missing(true); let pool = SqlitePoolOptions::new() .max_connections(5) .connect_with(options)