diff --git a/src/api.rs b/src/api.rs index 2be028f..3f1aefb 100644 --- a/src/api.rs +++ b/src/api.rs @@ -83,6 +83,53 @@ impl Ord for ClientVersion { } } +fn normalize_query(raw_query: Option<&str>) -> (Option, Option) { + let Some(raw_query) = raw_query else { + return (None, None); + }; + + let mut query = raw_query.trim(); + if query.is_empty() { + return (None, None); + } + + while let Some(stripped) = query.strip_prefix('#') { + query = stripped.trim_start(); + } + + if query.is_empty() { + return (None, None); + } + + let literal_query = if query.len() >= 2 + && ((query.starts_with('"') && query.ends_with('"')) + || (query.starts_with('\'') && query.ends_with('\''))) + { + let inner = query[1..query.len() - 1].trim(); + if inner.is_empty() { + None + } else { + query = inner; + Some(inner.to_ascii_lowercase()) + } + } else { + None + }; + + (Some(query.to_string()), literal_query) +} + +fn video_matches_literal_query(video: &VideoItem, literal_query: &str) -> bool { + let contains_literal = |value: &str| value.to_ascii_lowercase().contains(literal_query); + + contains_literal(&video.title) + || video.uploader.as_deref().is_some_and(contains_literal) + || video + .tags + .as_ref() + .is_some_and(|tags| tags.iter().any(|tag| contains_literal(tag))) +} + pub fn config(cfg: &mut web::ServiceConfig) { cfg.service( web::resource("/status") @@ -145,7 +192,7 @@ async fn status(req: HttpRequest) -> Result { } async fn videos_post( - mut video_request: web::types::Json, + video_request: web::types::Json, cache: web::types::State, pool: web::types::State, requester: web::types::State, @@ -159,12 +206,6 @@ async fn videos_post( }, _ => ClientVersion::new(999, 0, "Hot%20Tub".to_string()), }; - match video_request.query.as_deref() { - Some(query) if query.starts_with("#") => { - video_request.query = Some(query.trim_start_matches("#").to_string()); - } - _ => {} - } let requester = requester.get_ref().clone(); // Ensure "videos" table exists with two string columns. match pool.get() { @@ -200,10 +241,7 @@ async fn videos_post( .unwrap_or("all") .to_string(); let sort: String = video_request.sort.as_deref().unwrap_or("date").to_string(); - let mut query: Option = video_request.query.clone(); - if video_request.query.as_deref() == Some("") { - query = None; - } + let (query, literal_query) = normalize_query(video_request.query.as_deref()); let page: u8 = video_request .page .as_ref() @@ -306,6 +344,10 @@ async fn videos_post( .collect(); } + if let Some(literal_query) = literal_query.as_deref() { + video_items.retain(|video| video_matches_literal_query(video, literal_query)); + } + videos.items = video_items.clone(); if video_items.len() == 0 { videos.pageInfo = PageInfo {