Search by media type
This commit is contained in:
parent
e679d167fc
commit
8c9bdbb58e
8 changed files with 115 additions and 74 deletions
25
Cargo.lock
generated
25
Cargo.lock
generated
|
|
@ -3048,6 +3048,7 @@ dependencies = [
|
||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"strum 0.27.1",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
@ -3103,7 +3104,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"strum",
|
"strum 0.26.3",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"time",
|
"time",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
@ -3678,6 +3679,28 @@ version = "0.26.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.27.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
|
||||||
|
dependencies = [
|
||||||
|
"strum_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.27.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
|
||||||
|
dependencies = [
|
||||||
|
"heck 0.5.0",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn 2.0.100",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ time = "0.3.41"
|
||||||
tokio = { version = "1.44.1", features = ["full"] }
|
tokio = { version = "1.44.1", features = ["full"] }
|
||||||
tower-http = { version = "0.6.2", features = ["fs"] }
|
tower-http = { version = "0.6.2", features = ["fs"] }
|
||||||
tower-sessions = "0.14.0"
|
tower-sessions = "0.14.0"
|
||||||
|
strum = { version = "0.27.1", features = ["derive"] }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true
|
strip = true
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,10 @@ Still very much an early WIP.
|
||||||
|
|
||||||
### Possible roadmap
|
### Possible roadmap
|
||||||
|
|
||||||
- [ ] Caching
|
- [ ] Display thumbnails on post selection
|
||||||
- [ ] Text media
|
- [ ] Text media
|
||||||
- [ ] Improve CSS
|
- [ ] Improve CSS
|
||||||
- [ ] User management
|
- [ ] User management
|
||||||
- [ ] Display thumbnails on post selection
|
|
||||||
- [ ] Testing
|
- [ ] Testing
|
||||||
- [ ] Lossless compression
|
- [ ] Lossless compression
|
||||||
- [ ] Migrate to Cot...?
|
- [ ] Migrate to Cot...?
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ pub(crate) mod config;
|
||||||
pub(crate) mod entities;
|
pub(crate) mod entities;
|
||||||
pub(crate) mod error;
|
pub(crate) mod error;
|
||||||
pub(crate) mod query;
|
pub(crate) mod query;
|
||||||
pub(crate) mod rating;
|
pub(crate) mod tags;
|
||||||
pub(crate) mod video;
|
pub(crate) mod video;
|
||||||
pub(crate) mod views;
|
pub(crate) mod views;
|
||||||
|
|
||||||
|
|
@ -35,9 +35,6 @@ use crate::entities::{prelude::SameyUser, samey_user};
|
||||||
pub use crate::error::SameyError;
|
pub use crate::error::SameyError;
|
||||||
use crate::views::*;
|
use crate::views::*;
|
||||||
|
|
||||||
pub(crate) const NEGATIVE_PREFIX: &str = "-";
|
|
||||||
pub(crate) const RATING_PREFIX: &str = "rating:";
|
|
||||||
|
|
||||||
#[derive(rust_embed::Embed)]
|
#[derive(rust_embed::Embed)]
|
||||||
#[folder = "static/"]
|
#[folder = "static/"]
|
||||||
struct Asset;
|
struct Asset;
|
||||||
|
|
|
||||||
37
src/query.rs
37
src/query.rs
|
|
@ -9,12 +9,13 @@ use sea_orm::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
NEGATIVE_PREFIX, RATING_PREFIX, SameyError,
|
SameyError,
|
||||||
auth::User,
|
auth::User,
|
||||||
entities::{
|
entities::{
|
||||||
prelude::{SameyPool, SameyPoolPost, SameyPost, SameyTag, SameyTagPost},
|
prelude::{SameyPool, SameyPoolPost, SameyPost, SameyTag, SameyTagPost},
|
||||||
samey_pool, samey_pool_post, samey_post, samey_tag, samey_tag_post,
|
samey_pool, samey_pool_post, samey_post, samey_tag, samey_tag_post,
|
||||||
},
|
},
|
||||||
|
tags::{MEDIA_TYPE_PREFIX, NEGATIVE_PREFIX, RATING_PREFIX},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, FromQueryResult)]
|
#[derive(Debug, FromQueryResult)]
|
||||||
|
|
@ -38,17 +39,23 @@ pub(crate) fn search_posts(
|
||||||
let mut exclude_tags = HashSet::<String>::new();
|
let mut exclude_tags = HashSet::<String>::new();
|
||||||
let mut include_ratings = HashSet::<String>::new();
|
let mut include_ratings = HashSet::<String>::new();
|
||||||
let mut exclude_ratings = HashSet::<String>::new();
|
let mut exclude_ratings = HashSet::<String>::new();
|
||||||
|
let mut include_types = HashSet::<String>::new();
|
||||||
|
let mut exclude_types = HashSet::<String>::new();
|
||||||
if let Some(tags) = tags {
|
if let Some(tags) = tags {
|
||||||
for mut tag in tags.iter().map(|tag| tag.to_lowercase()) {
|
for tag in tags.iter().map(|tag| tag.to_lowercase()) {
|
||||||
if tag.starts_with(NEGATIVE_PREFIX) {
|
if let Some(negative_tag) = tag.strip_prefix(NEGATIVE_PREFIX) {
|
||||||
if tag.as_str()[NEGATIVE_PREFIX.len()..].starts_with(RATING_PREFIX) {
|
if let Some(negative_rating_tag) = negative_tag.strip_prefix(RATING_PREFIX) {
|
||||||
exclude_ratings
|
exclude_ratings.insert(negative_rating_tag.into());
|
||||||
.insert(tag.split_off(NEGATIVE_PREFIX.len() + RATING_PREFIX.len()));
|
} else if let Some(negative_type_tag) = negative_tag.strip_prefix(MEDIA_TYPE_PREFIX)
|
||||||
|
{
|
||||||
|
exclude_types.insert(negative_type_tag.into());
|
||||||
} else {
|
} else {
|
||||||
exclude_tags.insert(tag.split_off(NEGATIVE_PREFIX.len()));
|
exclude_tags.insert(negative_tag.into());
|
||||||
}
|
}
|
||||||
} else if tag.starts_with(RATING_PREFIX) {
|
} else if let Some(rating_tag) = tag.strip_prefix(RATING_PREFIX) {
|
||||||
include_ratings.insert(tag.split_off(RATING_PREFIX.len()));
|
include_ratings.insert(rating_tag.into());
|
||||||
|
} else if let Some(type_tag) = tag.strip_prefix(MEDIA_TYPE_PREFIX) {
|
||||||
|
include_types.insert(type_tag.into());
|
||||||
} else {
|
} else {
|
||||||
include_tags.insert(tag);
|
include_tags.insert(tag);
|
||||||
}
|
}
|
||||||
|
|
@ -81,6 +88,12 @@ pub(crate) fn search_posts(
|
||||||
if !exclude_ratings.is_empty() {
|
if !exclude_ratings.is_empty() {
|
||||||
query = query.filter(samey_post::Column::Rating.is_not_in(exclude_ratings))
|
query = query.filter(samey_post::Column::Rating.is_not_in(exclude_ratings))
|
||||||
}
|
}
|
||||||
|
if !include_types.is_empty() {
|
||||||
|
query = query.filter(samey_post::Column::MediaType.is_in(include_types))
|
||||||
|
}
|
||||||
|
if !exclude_types.is_empty() {
|
||||||
|
query = query.filter(samey_post::Column::MediaType.is_not_in(exclude_types))
|
||||||
|
}
|
||||||
query
|
query
|
||||||
} else {
|
} else {
|
||||||
let mut query = SameyPost::find()
|
let mut query = SameyPost::find()
|
||||||
|
|
@ -147,6 +160,12 @@ pub(crate) fn search_posts(
|
||||||
if !exclude_ratings.is_empty() {
|
if !exclude_ratings.is_empty() {
|
||||||
query = query.filter(samey_post::Column::Rating.is_not_in(exclude_ratings))
|
query = query.filter(samey_post::Column::Rating.is_not_in(exclude_ratings))
|
||||||
}
|
}
|
||||||
|
if !include_types.is_empty() {
|
||||||
|
query = query.filter(samey_post::Column::MediaType.is_in(include_types))
|
||||||
|
}
|
||||||
|
if !exclude_types.is_empty() {
|
||||||
|
query = query.filter(samey_post::Column::MediaType.is_not_in(exclude_types))
|
||||||
|
}
|
||||||
query
|
query
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub(crate) enum Rating {
|
|
||||||
Unrated,
|
|
||||||
Safe,
|
|
||||||
Questionable,
|
|
||||||
Explicit,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Rating {
|
|
||||||
fn from(value: String) -> Self {
|
|
||||||
match value.as_ref() {
|
|
||||||
"s" => Self::Safe,
|
|
||||||
"q" => Self::Questionable,
|
|
||||||
"e" => Self::Explicit,
|
|
||||||
_ => Self::Unrated,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Rating {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Rating::Unrated => f.write_str("Unrated"),
|
|
||||||
Rating::Safe => f.write_str("Safe"),
|
|
||||||
Rating::Questionable => f.write_str("Questionable"),
|
|
||||||
Rating::Explicit => f.write_str("Explicit"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
23
src/tags.rs
Normal file
23
src/tags.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
pub(crate) const NEGATIVE_PREFIX: &str = "-";
|
||||||
|
pub(crate) const RATING_PREFIX: &str = "rating:";
|
||||||
|
pub(crate) const MEDIA_TYPE_PREFIX: &str = "type:";
|
||||||
|
|
||||||
|
#[derive(strum::EnumIter, strum::Display, Debug)]
|
||||||
|
pub(crate) enum Rating {
|
||||||
|
#[strum(serialize = "u")]
|
||||||
|
Unrated,
|
||||||
|
#[strum(serialize = "s")]
|
||||||
|
Safe,
|
||||||
|
#[strum(serialize = "q")]
|
||||||
|
Questionable,
|
||||||
|
#[strum(serialize = "e")]
|
||||||
|
Explicit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(strum::EnumIter, strum::Display, Debug)]
|
||||||
|
pub(crate) enum MediaType {
|
||||||
|
#[strum(serialize = "image")]
|
||||||
|
Image,
|
||||||
|
#[strum(serialize = "video")]
|
||||||
|
Video,
|
||||||
|
}
|
||||||
40
src/views.rs
40
src/views.rs
|
|
@ -24,10 +24,11 @@ use sea_orm::{
|
||||||
ModelTrait, PaginatorTrait, QueryFilter, QuerySelect,
|
ModelTrait, PaginatorTrait, QueryFilter, QuerySelect,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
use tokio::{task::spawn_blocking, try_join};
|
use tokio::{task::spawn_blocking, try_join};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AppState, NEGATIVE_PREFIX, RATING_PREFIX,
|
AppState,
|
||||||
auth::{AuthSession, Credentials, User},
|
auth::{AuthSession, Credentials, User},
|
||||||
config::{AGE_CONFIRMATION_KEY, APPLICATION_NAME_KEY, BASE_URL_KEY},
|
config::{AGE_CONFIRMATION_KEY, APPLICATION_NAME_KEY, BASE_URL_KEY},
|
||||||
entities::{
|
entities::{
|
||||||
|
|
@ -43,6 +44,7 @@ use crate::{
|
||||||
PoolPost, PostOverview, PostPoolData, clean_dangling_tags, filter_posts_by_user,
|
PoolPost, PostOverview, PostPoolData, clean_dangling_tags, filter_posts_by_user,
|
||||||
get_pool_data_for_post, get_posts_in_pool, get_tags_for_post, search_posts,
|
get_pool_data_for_post, get_posts_in_pool, get_tags_for_post, search_posts,
|
||||||
},
|
},
|
||||||
|
tags::{MEDIA_TYPE_PREFIX, MediaType, NEGATIVE_PREFIX, RATING_PREFIX, Rating},
|
||||||
video::{generate_thumbnail, get_dimensions_for_video},
|
video::{generate_thumbnail, get_dimensions_for_video},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -531,13 +533,17 @@ pub(crate) async fn search_tags(
|
||||||
vec![]
|
vec![]
|
||||||
} else if let Some(stripped_tag) = tag.strip_prefix(NEGATIVE_PREFIX) {
|
} else if let Some(stripped_tag) = tag.strip_prefix(NEGATIVE_PREFIX) {
|
||||||
if stripped_tag.starts_with(RATING_PREFIX) {
|
if stripped_tag.starts_with(RATING_PREFIX) {
|
||||||
[
|
Rating::iter()
|
||||||
format!("{}u", RATING_PREFIX),
|
.map(|rating| format!("{}{}", RATING_PREFIX, rating))
|
||||||
format!("{}s", RATING_PREFIX),
|
.filter(|t| t.starts_with(stripped_tag))
|
||||||
format!("{}q", RATING_PREFIX),
|
.map(|tag| SearchTag {
|
||||||
format!("{}e", RATING_PREFIX),
|
value: format!("-{}", &tag),
|
||||||
]
|
name: tag,
|
||||||
.into_iter()
|
})
|
||||||
|
.collect()
|
||||||
|
} else if stripped_tag.starts_with(MEDIA_TYPE_PREFIX) {
|
||||||
|
MediaType::iter()
|
||||||
|
.map(|rating| format!("{}{}", MEDIA_TYPE_PREFIX, rating))
|
||||||
.filter(|t| t.starts_with(stripped_tag))
|
.filter(|t| t.starts_with(stripped_tag))
|
||||||
.map(|tag| SearchTag {
|
.map(|tag| SearchTag {
|
||||||
value: format!("-{}", &tag),
|
value: format!("-{}", &tag),
|
||||||
|
|
@ -561,13 +567,17 @@ pub(crate) async fn search_tags(
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
} else if tag.starts_with(RATING_PREFIX) {
|
} else if tag.starts_with(RATING_PREFIX) {
|
||||||
[
|
Rating::iter()
|
||||||
format!("{}u", RATING_PREFIX),
|
.map(|rating| format!("{}{}", RATING_PREFIX, rating))
|
||||||
format!("{}s", RATING_PREFIX),
|
.filter(|t| t.starts_with(tag))
|
||||||
format!("{}q", RATING_PREFIX),
|
.map(|tag| SearchTag {
|
||||||
format!("{}e", RATING_PREFIX),
|
value: tag.clone(),
|
||||||
]
|
name: tag,
|
||||||
.into_iter()
|
})
|
||||||
|
.collect()
|
||||||
|
} else if tag.starts_with(MEDIA_TYPE_PREFIX) {
|
||||||
|
MediaType::iter()
|
||||||
|
.map(|rating| format!("{}{}", MEDIA_TYPE_PREFIX, rating))
|
||||||
.filter(|t| t.starts_with(tag))
|
.filter(|t| t.starts_with(tag))
|
||||||
.map(|tag| SearchTag {
|
.map(|tag| SearchTag {
|
||||||
value: tag.clone(),
|
value: tag.clone(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue