mirror of
https://github.com/Arcadia-Solutions/arcadia.git
synced 2025-12-19 08:19:33 -06:00
feat: better typing on a series page
This commit is contained in:
@@ -5,7 +5,12 @@ use actix_web::{
|
|||||||
};
|
};
|
||||||
use arcadia_common::error::Result;
|
use arcadia_common::error::Result;
|
||||||
use arcadia_storage::{
|
use arcadia_storage::{
|
||||||
models::series::SeriesAndTitleGroupHierarchyLite, redis::RedisPoolInterface,
|
models::{
|
||||||
|
common::OrderByDirection,
|
||||||
|
series::SeriesAndTitleGroupHierarchyLite,
|
||||||
|
torrent::{TorrentSearch, TorrentSearchOrderByColumn},
|
||||||
|
},
|
||||||
|
redis::RedisPoolInterface,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use utoipa::IntoParams;
|
use utoipa::IntoParams;
|
||||||
@@ -31,5 +36,25 @@ pub async fn exec<R: RedisPoolInterface + 'static>(
|
|||||||
) -> Result<HttpResponse> {
|
) -> Result<HttpResponse> {
|
||||||
let series = arc.pool.find_series(&query.id).await?;
|
let series = arc.pool.find_series(&query.id).await?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(series))
|
let search_form = TorrentSearch {
|
||||||
|
series_id: Some(query.id),
|
||||||
|
page: 1,
|
||||||
|
page_size: i64::MAX,
|
||||||
|
order_by_column: TorrentSearchOrderByColumn::TitleGroupOriginalReleaseDate,
|
||||||
|
order_by_direction: OrderByDirection::Desc,
|
||||||
|
title_group_include_empty_groups: true,
|
||||||
|
title_group_name: None,
|
||||||
|
torrent_reported: None,
|
||||||
|
torrent_staff_checked: None,
|
||||||
|
torrent_created_by_id: None,
|
||||||
|
torrent_snatched_by_id: None,
|
||||||
|
artist_id: None,
|
||||||
|
collage_id: None,
|
||||||
|
};
|
||||||
|
let title_groups_in_series = arc.pool.search_torrents(&search_form, None).await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(SeriesAndTitleGroupHierarchyLite {
|
||||||
|
series,
|
||||||
|
title_groups: title_groups_in_series.results,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ pub async fn exec<R: RedisPoolInterface + 'static>(
|
|||||||
order_by_direction: OrderByDirection::Desc,
|
order_by_direction: OrderByDirection::Desc,
|
||||||
artist_id: None,
|
artist_id: None,
|
||||||
collage_id: None,
|
collage_id: None,
|
||||||
|
series_id: None,
|
||||||
};
|
};
|
||||||
let uploaded_torrents = arc
|
let uploaded_torrents = arc
|
||||||
.pool
|
.pool
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ pub async fn exec<R: RedisPoolInterface + 'static>(
|
|||||||
order_by_direction: OrderByDirection::Desc,
|
order_by_direction: OrderByDirection::Desc,
|
||||||
artist_id: None,
|
artist_id: None,
|
||||||
collage_id: None,
|
collage_id: None,
|
||||||
|
series_id: None,
|
||||||
};
|
};
|
||||||
let uploaded_torrents = arc
|
let uploaded_torrents = arc
|
||||||
.pool
|
.pool
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ async fn test_find_torrents_by_external_link(pool: PgPool) {
|
|||||||
page_size: 50,
|
page_size: 50,
|
||||||
order_by_column: TorrentSearchOrderByColumn::TorrentCreatedAt,
|
order_by_column: TorrentSearchOrderByColumn::TorrentCreatedAt,
|
||||||
order_by_direction: OrderByDirection::Desc,
|
order_by_direction: OrderByDirection::Desc,
|
||||||
|
series_id: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let query = serde_urlencoded::to_string(query).unwrap();
|
let query = serde_urlencoded::to_string(query).unwrap();
|
||||||
@@ -244,6 +245,7 @@ async fn test_find_torrents_by_name(pool: PgPool) {
|
|||||||
page_size: 50,
|
page_size: 50,
|
||||||
order_by_column: TorrentSearchOrderByColumn::TorrentCreatedAt,
|
order_by_column: TorrentSearchOrderByColumn::TorrentCreatedAt,
|
||||||
order_by_direction: OrderByDirection::Desc,
|
order_by_direction: OrderByDirection::Desc,
|
||||||
|
series_id: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let query = serde_urlencoded::to_string(query).unwrap();
|
let query = serde_urlencoded::to_string(query).unwrap();
|
||||||
@@ -300,6 +302,7 @@ async fn test_find_torrents_no_link_or_name_provided(pool: PgPool) {
|
|||||||
page_size: 50,
|
page_size: 50,
|
||||||
order_by_column: TorrentSearchOrderByColumn::TorrentCreatedAt,
|
order_by_column: TorrentSearchOrderByColumn::TorrentCreatedAt,
|
||||||
order_by_direction: OrderByDirection::Desc,
|
order_by_direction: OrderByDirection::Desc,
|
||||||
|
series_id: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let query = serde_urlencoded::to_string(query).unwrap();
|
let query = serde_urlencoded::to_string(query).unwrap();
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"db_name": "PostgreSQL",
|
|
||||||
"query": "\n SELECT\n jsonb_build_object(\n 'series', to_jsonb(s),\n 'title_groups', COALESCE(\n jsonb_agg(tgd.title_group_data),\n '[]'::jsonb\n )\n ) AS series_and_title_groups\n FROM\n series s\n LEFT JOIN\n title_groups tg ON s.id = tg.series_id\n LEFT JOIN\n get_title_groups_and_edition_group_and_torrents_lite AS tgd ON tg.id = tgd.title_group_id\n WHERE\n s.id = $1\n GROUP BY\n s.id, s.*;\n ",
|
|
||||||
"describe": {
|
|
||||||
"columns": [
|
|
||||||
{
|
|
||||||
"ordinal": 0,
|
|
||||||
"name": "series_and_title_groups",
|
|
||||||
"type_info": "Jsonb"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Int8"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nullable": [
|
|
||||||
null
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hash": "09337987d244feeb7b38251eac5791f9d499388dd01ab4d369e963ed3ecf3f95"
|
|
||||||
}
|
|
||||||
70
backend/storage/.sqlx/query-9bd01434dd614efca467327891d8038b8f431cf6188a9ed81ee9a925f7c32208.json
generated
Normal file
70
backend/storage/.sqlx/query-9bd01434dd614efca467327891d8038b8f431cf6188a9ed81ee9a925f7c32208.json
generated
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT * FROM series\n WHERE series.id = $1\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id",
|
||||||
|
"type_info": "Int8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "name",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "description",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "tags",
|
||||||
|
"type_info": "TextArray"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "covers",
|
||||||
|
"type_info": "TextArray"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "banners",
|
||||||
|
"type_info": "TextArray"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 6,
|
||||||
|
"name": "created_by_id",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 7,
|
||||||
|
"name": "created_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 8,
|
||||||
|
"name": "updated_at",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Int8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "9bd01434dd614efca467327891d8038b8f431cf6188a9ed81ee9a925f7c32208"
|
||||||
|
}
|
||||||
145
backend/storage/.sqlx/query-baa37b12c8a0b2ff7ac0419faac8bbe6c2763b894727e823d6b343941efcd160.json
generated
Normal file
145
backend/storage/.sqlx/query-baa37b12c8a0b2ff7ac0419faac8bbe6c2763b894727e823d6b343941efcd160.json
generated
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "\n SELECT title_group_id AS \"id!\", title_group_name AS \"name!\", title_group_covers AS \"covers!\",\n title_group_category AS \"category!: _\", title_group_content_type AS \"content_type!: _\", title_group_tag_names AS \"tags!\",\n title_group_original_release_date AS \"original_release_date!\", title_group_platform AS \"platform!: _\",\n '[]'::jsonb AS \"edition_groups!: _\",\n '[]'::jsonb AS \"affiliated_artists!: _\"\n\n FROM title_group_hierarchy_lite tgh\n\n WHERE ($4::BOOLEAN IS NULL OR tgh.torrent_staff_checked = $4)\n AND ($5::BOOLEAN IS NULL OR tgh.torrent_reported = $5)\n AND (\n $7::INT IS NULL OR\n -- don't return torrents created as anonymous\n -- unless the requesting user is the uploader\n (tgh.torrent_created_by_id = $7 AND (\n tgh.torrent_created_by_id = $8 OR\n NOT tgh.torrent_uploaded_as_anonymous)\n )\n )\n AND (\n $9::BIGINT IS NULL OR\n EXISTS (SELECT 1 FROM affiliated_artists aa WHERE aa.title_group_id = tgh.title_group_id AND aa.artist_id = $9)\n )\n -- name filter (partial match) or external link match or series name match\n AND (\n $10::TEXT IS NULL OR\n tgh.title_group_name ILIKE '%' || $10 || '%' ESCAPE '\\' OR\n tgh.title_group_series_name ILIKE '%' || $10 || '%' ESCAPE '\\'\n )\n AND ($11::TEXT IS NULL OR $11 = ANY(tgh.title_group_external_links))\n AND ($12::BOOLEAN IS TRUE OR tgh.torrent_id IS NOT NULL)\n AND ($13::BIGINT IS NULL OR tgh.title_group_series_id = $13)\n\n GROUP BY title_group_id, title_group_name, title_group_covers, title_group_category,\n title_group_content_type, title_group_tag_names, title_group_original_release_date, title_group_platform\n\n ORDER BY\n CASE WHEN $1 = 'title_group_original_release_date' AND $6 = 'asc' THEN title_group_original_release_date END ASC,\n CASE WHEN $1 = 'title_group_original_release_date' AND $6 = 'desc' THEN title_group_original_release_date END DESC,\n CASE WHEN $1 = 'torrent_size' AND $6 = 'asc' THEN MAX(torrent_size) END ASC,\n CASE WHEN $1 = 'torrent_size' AND $6 = 'desc' THEN MAX(torrent_size) END DESC,\n CASE WHEN $1 = 'torrent_created_at' AND $6 = 'asc' THEN MAX(torrent_created_at) END ASC,\n CASE WHEN $1 = 'torrent_created_at' AND $6 = 'desc' THEN MAX(torrent_created_at) END DESC,\n title_group_original_release_date ASC\n\n LIMIT $2 OFFSET $3\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"ordinal": 0,
|
||||||
|
"name": "id!",
|
||||||
|
"type_info": "Int4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 1,
|
||||||
|
"name": "name!",
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 2,
|
||||||
|
"name": "covers!",
|
||||||
|
"type_info": "TextArray"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 3,
|
||||||
|
"name": "category!: _",
|
||||||
|
"type_info": {
|
||||||
|
"Custom": {
|
||||||
|
"name": "title_group_category_enum",
|
||||||
|
"kind": {
|
||||||
|
"Enum": [
|
||||||
|
"Ep",
|
||||||
|
"Album",
|
||||||
|
"Single",
|
||||||
|
"Soundtrack",
|
||||||
|
"Anthology",
|
||||||
|
"Compilation",
|
||||||
|
"Remix",
|
||||||
|
"Bootleg",
|
||||||
|
"Mixtape",
|
||||||
|
"ConcertRecording",
|
||||||
|
"DjMix",
|
||||||
|
"FeatureFilm",
|
||||||
|
"ShortFilm",
|
||||||
|
"Game",
|
||||||
|
"Program",
|
||||||
|
"Illustrated",
|
||||||
|
"Periodical",
|
||||||
|
"Book",
|
||||||
|
"Article",
|
||||||
|
"Manual",
|
||||||
|
"Other"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
|
"name": "content_type!: _",
|
||||||
|
"type_info": {
|
||||||
|
"Custom": {
|
||||||
|
"name": "content_type_enum",
|
||||||
|
"kind": {
|
||||||
|
"Enum": [
|
||||||
|
"movie",
|
||||||
|
"video",
|
||||||
|
"tv_show",
|
||||||
|
"music",
|
||||||
|
"podcast",
|
||||||
|
"software",
|
||||||
|
"book",
|
||||||
|
"collection"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
|
"name": "tags!",
|
||||||
|
"type_info": "VarcharArray"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 6,
|
||||||
|
"name": "original_release_date!",
|
||||||
|
"type_info": "Timestamptz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 7,
|
||||||
|
"name": "platform!: _",
|
||||||
|
"type_info": {
|
||||||
|
"Custom": {
|
||||||
|
"name": "platform_enum",
|
||||||
|
"kind": {
|
||||||
|
"Enum": [
|
||||||
|
"Linux",
|
||||||
|
"MacOS",
|
||||||
|
"Windows",
|
||||||
|
"Xbox"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 8,
|
||||||
|
"name": "edition_groups!: _",
|
||||||
|
"type_info": "Jsonb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 9,
|
||||||
|
"name": "affiliated_artists!: _",
|
||||||
|
"type_info": "Jsonb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Int8",
|
||||||
|
"Int8",
|
||||||
|
"Bool",
|
||||||
|
"Bool",
|
||||||
|
"Text",
|
||||||
|
"Int4",
|
||||||
|
"Int4",
|
||||||
|
"Int8",
|
||||||
|
"Text",
|
||||||
|
"Text",
|
||||||
|
"Bool",
|
||||||
|
"Int8"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "baa37b12c8a0b2ff7ac0419faac8bbe6c2763b894727e823d6b343941efcd160"
|
||||||
|
}
|
||||||
@@ -457,6 +457,7 @@ pub struct TorrentSearch {
|
|||||||
// link to other tables
|
// link to other tables
|
||||||
pub artist_id: Option<i64>,
|
pub artist_id: Option<i64>,
|
||||||
pub collage_id: Option<i32>,
|
pub collage_id: Option<i32>,
|
||||||
|
pub series_id: Option<i64>,
|
||||||
// pagination and ordering
|
// pagination and ordering
|
||||||
pub page: i64,
|
pub page: i64,
|
||||||
pub page_size: i64,
|
pub page_size: i64,
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ impl ConnectionPool {
|
|||||||
torrent_reported: None,
|
torrent_reported: None,
|
||||||
torrent_snatched_by_id: None,
|
torrent_snatched_by_id: None,
|
||||||
torrent_staff_checked: None,
|
torrent_staff_checked: None,
|
||||||
|
series_id: None,
|
||||||
order_by_direction: OrderByDirection::Desc,
|
order_by_direction: OrderByDirection::Desc,
|
||||||
order_by_column: TorrentSearchOrderByColumn::TitleGroupOriginalReleaseDate,
|
order_by_column: TorrentSearchOrderByColumn::TitleGroupOriginalReleaseDate,
|
||||||
collage_id: None,
|
collage_id: None,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use arcadia_common::error::{Error, Result};
|
use arcadia_common::error::{Error, Result};
|
||||||
use serde_json::Value;
|
|
||||||
use sqlx::{query_as_unchecked, query_scalar};
|
use sqlx::{query_as_unchecked, query_scalar};
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
@@ -32,27 +31,12 @@ impl ConnectionPool {
|
|||||||
Ok(created_series)
|
Ok(created_series)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_series(&self, series_id: &i64) -> Result<Value> {
|
pub async fn find_series(&self, series_id: &i64) -> Result<Series> {
|
||||||
let found_series = sqlx::query!(
|
let series = sqlx::query_as!(
|
||||||
|
Series,
|
||||||
r#"
|
r#"
|
||||||
SELECT
|
SELECT * FROM series
|
||||||
jsonb_build_object(
|
WHERE series.id = $1
|
||||||
'series', to_jsonb(s),
|
|
||||||
'title_groups', COALESCE(
|
|
||||||
jsonb_agg(tgd.title_group_data),
|
|
||||||
'[]'::jsonb
|
|
||||||
)
|
|
||||||
) AS series_and_title_groups
|
|
||||||
FROM
|
|
||||||
series s
|
|
||||||
LEFT JOIN
|
|
||||||
title_groups tg ON s.id = tg.series_id
|
|
||||||
LEFT JOIN
|
|
||||||
get_title_groups_and_edition_group_and_torrents_lite AS tgd ON tg.id = tgd.title_group_id
|
|
||||||
WHERE
|
|
||||||
s.id = $1
|
|
||||||
GROUP BY
|
|
||||||
s.id, s.*;
|
|
||||||
"#,
|
"#,
|
||||||
series_id
|
series_id
|
||||||
)
|
)
|
||||||
@@ -60,8 +44,7 @@ impl ConnectionPool {
|
|||||||
.await
|
.await
|
||||||
.map_err(|_| Error::SeriesWithIdNotFound(*series_id))?;
|
.map_err(|_| Error::SeriesWithIdNotFound(*series_id))?;
|
||||||
|
|
||||||
// Ok(serde_json::from_value(found_series.series_and_groups.unwrap()).unwrap())
|
Ok(series)
|
||||||
Ok(found_series.series_and_title_groups.unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn search_series(&self, form: &SearchSeriesQuery) -> Result<SeriesSearchResponse> {
|
pub async fn search_series(&self, form: &SearchSeriesQuery) -> Result<SeriesSearchResponse> {
|
||||||
|
|||||||
@@ -454,6 +454,7 @@ impl ConnectionPool {
|
|||||||
)
|
)
|
||||||
AND ($11::TEXT IS NULL OR $11 = ANY(tgh.title_group_external_links))
|
AND ($11::TEXT IS NULL OR $11 = ANY(tgh.title_group_external_links))
|
||||||
AND ($12::BOOLEAN IS TRUE OR tgh.torrent_id IS NOT NULL)
|
AND ($12::BOOLEAN IS TRUE OR tgh.torrent_id IS NOT NULL)
|
||||||
|
AND ($13::BIGINT IS NULL OR tgh.title_group_series_id = $13)
|
||||||
|
|
||||||
GROUP BY title_group_id, title_group_name, title_group_covers, title_group_category,
|
GROUP BY title_group_id, title_group_name, title_group_covers, title_group_category,
|
||||||
title_group_content_type, title_group_tag_names, title_group_original_release_date, title_group_platform
|
title_group_content_type, title_group_tag_names, title_group_original_release_date, title_group_platform
|
||||||
@@ -480,7 +481,8 @@ impl ConnectionPool {
|
|||||||
form.artist_id,
|
form.artist_id,
|
||||||
name_filter,
|
name_filter,
|
||||||
external_link_filter,
|
external_link_filter,
|
||||||
form.title_group_include_empty_groups
|
form.title_group_include_empty_groups,
|
||||||
|
form.series_id
|
||||||
)
|
)
|
||||||
.fetch_all(self.borrow())
|
.fetch_all(self.borrow())
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ const router = createRouter({
|
|||||||
},
|
},
|
||||||
component: () => import('../views/torrent_request/TorrentRequestView.vue'),
|
component: () => import('../views/torrent_request/TorrentRequestView.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/new-series',
|
||||||
|
name: 'CreateSeries',
|
||||||
|
component: () => import('../views/series/CreateOrEditSeriesView.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/series/:id',
|
path: '/series/:id',
|
||||||
name: 'Series',
|
name: 'Series',
|
||||||
|
|||||||
Reference in New Issue
Block a user