use std::time::{Duration, Instant}; use julid::Julid; use sqlx::{query_scalar, SqlitePool}; use crate::{util::year_to_epoch, ShowKind, User, Watch, WatchQuest}; const USER_EXISTS_QUERY: &str = "select count(*) from users where id = $1"; //-************************************************************************ // the omega user is the system ID, but has no actual power in the app //-************************************************************************ const OMEGA_ID: Julid = Julid::omega(); const BULK_INSERT: usize = 2_000; #[derive(Debug, sqlx::FromRow, Clone)] pub struct ImportImdbMovie { pub title: String, pub year: Option, pub runtime: Option, pub aid: String, } impl From for Watch { fn from(value: ImportImdbMovie) -> Self { Watch { id: OMEGA_ID, // this is ignored by the inserter title: value.title, release_date: year_to_epoch(value.year.as_deref()), length: value.runtime.and_then(|v| v.parse::().ok()), kind: ShowKind::Movie, metadata_url: Some(format!("https://imdb.com/title/{}/", &value.aid)), added_by: OMEGA_ID, } } } impl From<&ImportImdbMovie> for Watch { fn from(value: &ImportImdbMovie) -> Self { Watch { id: OMEGA_ID, title: value.title.to_string(), release_date: year_to_epoch(value.year.as_deref()), length: value.runtime.as_ref().and_then(|v| v.parse::().ok()), kind: ShowKind::Movie, metadata_url: Some(format!("https://imdb.com/title/{}/", value.aid)), added_by: OMEGA_ID, } } } //-************************************************************************ // utility functions for building CLI tools, currently just for benchmarking //-************************************************************************ pub async fn add_watch_quests(pool: &SqlitePool, quests: &[WatchQuest]) -> Result<(), ()> { let mut builder = sqlx::QueryBuilder::new("insert into watch_quests (user, watch) "); builder.push_values(quests, |mut b, quest| { let user = quest.user; let watch = quest.watch; //eprintln!("{user}, {watch}"); b.push_bind(user).push_bind(watch); }); let q = builder.build(); q.execute(pool).await.map_err(|e| { dbg!(e); })?; Ok(()) } pub async fn add_users(db_pool: &SqlitePool, users: &[User]) -> Result { let mut builder = sqlx::QueryBuilder::new("insert into users (username, displayname, email, pwhash)"); let start = Instant::now(); builder.push_values(users.iter(), |mut b, user| { b.push_bind(&user.username) .push_bind(&user.displayname) .push_bind(&user.email) .push_bind(&user.pwhash); }); let q = builder.build(); q.execute(db_pool).await.map_err(|_| ())?; let end = Instant::now(); let dur = end - start; Ok(dur) } pub async fn add_imdb_movies( w2w_db: &SqlitePool, movie_db: &SqlitePool, num: u32, ) -> Result { const IMDB_MOVIE_QUERY: &str = "select * from movie_titles where porn = 0 order by year, title asc limit ?"; let omega = ensure_omega(w2w_db).await; let movies: Vec = sqlx::query_as(IMDB_MOVIE_QUERY) .bind(num) .fetch_all(movie_db) .await .unwrap(); let start = Instant::now(); for movies in movies.as_slice().chunks(BULK_INSERT) { let mut builder = sqlx::QueryBuilder::new( "insert into watches (kind, title, length, release_date, added_by, metadata_url) ", ); builder.push_values(movies, |mut b, movie| { let movie: Watch = movie.into(); b.push_bind(ShowKind::Movie) .push_bind(movie.title) .push_bind(movie.length) .push_bind(movie.release_date) .push_bind(omega) .push_bind(movie.metadata_url); }); let q = builder.build(); q.execute(w2w_db).await.map_err(|_| ())?; } let end = Instant::now(); let dur = end - start; Ok(dur) } pub async fn ensure_omega(db_pool: &SqlitePool) -> Julid { if !check_omega_exists(db_pool).await { User::omega().try_insert(db_pool).await.unwrap(); } OMEGA_ID } async fn check_omega_exists(db_pool: &SqlitePool) -> bool { let count = query_scalar(USER_EXISTS_QUERY) .bind(OMEGA_ID) .fetch_one(db_pool) .await .unwrap_or(0); count > 0 } //-************************************************************************ //tests //-************************************************************************ #[cfg(test)] mod test { use tokio::runtime::Runtime; use super::*; #[test] fn ensure_omega_user() { let p = crate::db::get_db_pool(); let rt = Runtime::new().unwrap(); rt.block_on(async { dbg!("checking omega"); assert!(!check_omega_exists(&p).await); dbg!("no omega"); ensure_omega(&p).await; }); dbg!("maybe omega"); assert!(rt.block_on(check_omega_exists(&p))); } }