added title group search endpoint and refactor

This commit is contained in:
FrenchGithubUser
2025-03-20 20:43:28 +01:00
parent 8547ed681f
commit d8f9707693
15 changed files with 137 additions and 70 deletions

View File

@@ -23,4 +23,5 @@ rand = "0.9.0"
bip_metainfo = "0.12.0"
serde_bytes = "0.11"
actix-multipart = "0.7.2"
utoipa = "5"
# serde_postgres = "0.2.0"
# utoipa = "5"

View File

@@ -46,21 +46,22 @@ CREATE TABLE artists (
name VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
pictures TEXT [],
created_by INT,
created_by_id INT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
title_groups_amount INT NOT NULL DEFAULT 0,
edition_groups_amount INT NOT NULL DEFAULT 0,
torrents_amount INT NOT NULL DEFAULT 0,
seeders_amount INT NOT NULL DEFAULT 0,
leechers_amount INT NOT NULL DEFAULT 0,
snatches_amount INT NOT NULL DEFAULT 0,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE
FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE similar_artists (
artist_1 INT NOT NULL,
artist_2 INT NOT NULL,
PRIMARY KEY (artist_1, artist_2),
FOREIGN KEY (artist_1) REFERENCES artists(id) ON DELETE CASCADE,
FOREIGN KEY (artist_2) REFERENCES artists(id) ON DELETE CASCADE
artist_1_id INT NOT NULL,
artist_2_id INT NOT NULL,
PRIMARY KEY (artist_1_id, artist_2_id),
FOREIGN KEY (artist_1_id) REFERENCES artists(id) ON DELETE CASCADE,
FOREIGN KEY (artist_2_id) REFERENCES artists(id) ON DELETE CASCADE
);
CREATE TABLE master_groups (
id SERIAL PRIMARY KEY,
@@ -68,7 +69,7 @@ CREATE TABLE master_groups (
-- name_aliases VARCHAR(255)[],
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by INT NOT NULL,
created_by_id INT NOT NULL,
-- description TEXT NOT NULL,
-- original_language VARCHAR(50) NOT NULL,
-- country_from VARCHAR(50) NOT NULL,
@@ -77,15 +78,15 @@ CREATE TABLE master_groups (
-- covers TEXT[],
-- banners TEXT[],
-- fan_arts TEXT[],
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE
FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE
SET NULL
);
CREATE TABLE similar_master_groups (
group_1 INT NOT NULL,
group_2 INT NOT NULL,
PRIMARY KEY (group_1, group_2),
FOREIGN KEY (group_1) REFERENCES master_groups(id) ON DELETE CASCADE,
FOREIGN KEY (group_2) REFERENCES master_groups(id) ON DELETE CASCADE
group_1_id INT NOT NULL,
group_2_id INT NOT NULL,
PRIMARY KEY (group_1_id, group_2_id),
FOREIGN KEY (group_1_id) REFERENCES master_groups(id) ON DELETE CASCADE,
FOREIGN KEY (group_2_id) REFERENCES master_groups(id) ON DELETE CASCADE
);
CREATE TABLE series (
id SERIAL PRIMARY KEY,
@@ -95,14 +96,14 @@ CREATE TABLE series (
covers TEXT [],
fanarts TEXT [],
banners TEXT [],
created_by INT,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE
created_by_id INT,
FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TYPE content_type_enum AS ENUM (
'Movie',
'TV-Show',
'Music',
'Game',
'Software',
'Book',
'SiteRip'
);
@@ -131,12 +132,12 @@ CREATE TYPE category_enum AS ENUM (
);
CREATE TABLE title_groups (
id SERIAL PRIMARY KEY,
master_group INT,
master_group_id INT,
name TEXT NOT NULL,
name_aliases TEXT [],
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by INT NOT NULL,
created_by_id INT NOT NULL,
description TEXT NOT NULL,
original_language TEXT NOT NULL,
original_release_date TIMESTAMP NOT NULL,
@@ -149,31 +150,31 @@ CREATE TABLE title_groups (
category category_enum,
content_type content_type_enum NOT NULL,
public_ratings JSONB,
serie INT,
FOREIGN KEY (master_group) REFERENCES master_groups(id) ON DELETE
serie_id INT,
FOREIGN KEY (master_group_id) REFERENCES master_groups(id) ON DELETE
SET NULL,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE
FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE
SET NULL,
FOREIGN KEY (serie) REFERENCES series(id) ON DELETE
FOREIGN KEY (serie_id) REFERENCES series(id) ON DELETE
SET NULL
);
CREATE TABLE similar_title_groups (
group_1 INT NOT NULL,
group_2 INT NOT NULL,
PRIMARY KEY (group_1, group_2),
FOREIGN KEY (group_1) REFERENCES title_groups(id) ON DELETE CASCADE,
FOREIGN KEY (group_2) REFERENCES title_groups(id) ON DELETE CASCADE
group_1_id INT NOT NULL,
group_2_id INT NOT NULL,
PRIMARY KEY (group_1_id, group_2_id),
FOREIGN KEY (group_1_id) REFERENCES title_groups(id) ON DELETE CASCADE,
FOREIGN KEY (group_2_id) REFERENCES title_groups(id) ON DELETE CASCADE
);
CREATE TABLE affiliated_artists (
title_group INT NOT NULL,
artist INT NOT NULL,
title_group_id INT NOT NULL,
artist_id INT NOT NULL,
status TEXT NOT NULL,
nickname TEXT,
created_by INT NOT NULL,
created_by_id INT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
FOREIGN KEY (title_group) REFERENCES title_groups(id) ON DELETE CASCADE,
FOREIGN KEY (artist) REFERENCES artists(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE
FOREIGN KEY (title_group_id) REFERENCES title_groups(id) ON DELETE CASCADE,
FOREIGN KEY (artist_id) REFERENCES artists(id) ON DELETE CASCADE,
FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE
SET NULL
);
-- for web: if it is a DL or a RIP should be specified at the torrent level
@@ -199,20 +200,20 @@ CREATE TYPE source_enum AS ENUM (
);
CREATE TABLE edition_groups (
id SERIAL PRIMARY KEY,
title_group INT NOT NULL,
title_group_id INT NOT NULL,
name TEXT NOT NULL,
release_date TIMESTAMP NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by INT NOT NULL,
created_by_id INT NOT NULL,
description TEXT,
distributor VARCHAR(255),
covers TEXT [] NOT NULL,
external_links TEXT [] NOT NULL,
language TEXT,
source source_enum NOT NULL,
FOREIGN KEY (title_group) REFERENCES title_groups(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE
FOREIGN KEY (title_group_id) REFERENCES title_groups(id) ON DELETE CASCADE,
FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE
SET NULL
);
CREATE TYPE audio_codec_enum AS ENUM (
@@ -259,10 +260,10 @@ CREATE TYPE video_codec_enum AS ENUM(
CREATE TYPE features_enum AS ENUM('HDR', 'DV', 'Commentary', 'Remux', '3D');
CREATE TABLE torrents (
id SERIAL PRIMARY KEY,
edition_group INT NOT NULL,
edition_group_id INT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
created_by INT NOT NULL,
created_by_id INT NOT NULL,
release_name VARCHAR(500),
-- maybe change the size
release_group VARCHAR(30) NOT NULL,
@@ -276,8 +277,8 @@ CREATE TABLE torrents (
staff_checked BOOLEAN NOT NULL DEFAULT FALSE,
size BIGINT NOT NULL,
-- in bytes
FOREIGN KEY (edition_group) REFERENCES edition_groups(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE
FOREIGN KEY (edition_group_id) REFERENCES edition_groups(id) ON DELETE CASCADE,
FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE
SET NULL,
-- audio
duration INT NOT NULL,

View File

@@ -1,9 +1,11 @@
use std::collections::HashMap;
use actix_web::{HttpResponse, web};
use sqlx::PgPool;
use crate::{
models::{title_group::UserCreatedTitleGroup, user::User},
repositories::title_group_repository::create_title_group,
repositories::title_group_repository::{create_title_group, find_title_group},
};
pub async fn add_title_group(
@@ -18,3 +20,16 @@ pub async fn add_title_group(
})),
}
}
pub async fn get_title_group(
pool: web::Data<PgPool>,
query: web::Query<HashMap<String, String>>,
) -> HttpResponse {
let title_group_id = query.get("id").expect("id not found in query");
match find_title_group(&pool, title_group_id.parse::<i32>().unwrap()).await {
Ok(title_group) => HttpResponse::Ok().json(title_group),
Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
"error": err.to_string()
})),
}
}

View File

@@ -1,3 +1,4 @@
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use sqlx::prelude::FromRow;
@@ -5,6 +6,8 @@ use sqlx::prelude::FromRow;
pub struct Artist {
pub id: i32,
pub name: String,
pub created_at: NaiveDateTime,
pub created_by_id: i32,
pub description: String,
pub pictures: Option<Vec<String>>,
pub title_groups_amount: i32,
@@ -15,7 +18,7 @@ pub struct Artist {
pub snatches_amount: i32,
}
#[derive(Debug, Deserialize, FromRow)]
#[derive(Debug, Deserialize, Serialize, FromRow)]
pub struct SimilarArtists {
pub artist_1: i32,
pub artist_2: i32,

View File

@@ -35,12 +35,12 @@ pub enum Source {
#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct EditionGroup {
pub id: i32,
pub title_group: i32,
pub title_group_id: i32,
pub name: String, // edition name, not title name
pub release_date: NaiveDateTime, // public release
pub created_at: NaiveDateTime, // database entry creation
pub updated_at: NaiveDateTime,
pub created_by: i32,
pub created_by_id: i32,
pub description: Option<String>, // specific to the edition
pub distributor: Option<String>, // web: [web stores/distributors], physical: [shop if specific edition ?]
pub covers: Vec<String>,

View File

@@ -22,7 +22,7 @@ pub struct MasterGroup {
// pub name_aliases: Vec<String>,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub created_by: i32,
pub created_by_id: i32,
// pub description: String,
// pub original_language: String,
// pub country_from: String,

View File

@@ -9,7 +9,7 @@ pub struct Serie {
pub description: String,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub created_by: i32,
pub created_by_id: i32,
pub covers: Vec<String>,
pub fannarts: Vec<String>,
pub banners: Vec<String>,

View File

@@ -10,7 +10,7 @@ pub enum ContentType {
#[sqlx(rename = "TV-Show")]
TVShow,
Music,
Game,
Software,
Book,
SiteRip,
}
@@ -54,12 +54,12 @@ pub enum Category {
#[derive(Debug, Serialize, Deserialize, FromRow)]
pub struct TitleGroup {
pub id: i32,
pub master_group: Option<i32>, // only if master groups are needed for this type of content
pub master_group_id: Option<i32>, // only if master groups are needed for this type of content
pub name: String,
pub name_aliases: Vec<String>,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub created_by: i32,
pub created_by_id: i32,
pub description: String,
pub original_language: String,
pub original_release_date: NaiveDateTime,
@@ -71,11 +71,12 @@ pub struct TitleGroup {
// pub main_artists
// pub artists_affiliated (multiple categories, multiple in each category) (composer, remixer, actors, developers, etc.)
// pub entities_affiliated (multiple categories, mutliple in each category) (publisher, record label, franchise, etc.)
pub category: i32, // ((movie: feature film, short film), (music: ep, album, compilation))
pub category: Category, // ((movie: feature film, short film), (music: ep, album, compilation))
pub content_type: ContentType, // movies, tv shows, books, games, etc
pub tags: Vec<String>,
pub public_ratings: Option<Json<Value>>, // {service: rating}
pub serie: Option<i32>,
pub serie_id: Option<i32>,
// pub edition_groups: Option<Vec<EditionGroup>>,
}
#[derive(Debug, Serialize, Deserialize)]
@@ -114,7 +115,7 @@ pub struct UserCreatedTitleGroup {
pub embedded_links: Option<Json<Value>>,
// pub artists_affiliated: //(multiple categories, multiple in each category) (composer, remixer, actors, developers, etc.)
// pub entities_affiliated (multiple categories, mutliple in each category) (publisher, record label, franchise, etc.)
pub category: i32, // ((movie: feature film, short film), (music: ep, album, compilation))
pub category: Category, // ((movie: feature film, short film), (music: ep, album, compilation))
pub content_type: ContentType, // movies, tv shows, books, games, etc
pub tags: Vec<String>,
pub tagline: Option<String>,

View File

@@ -106,10 +106,10 @@ impl FromStr for Features {
#[derive(Debug, Serialize, FromRow)]
pub struct Torrent {
pub id: i32,
pub edition_group: i32,
pub edition_group_id: i32,
pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime,
pub created_by: i32,
pub created_by_id: i32,
pub release_name: Option<String>,
pub release_group: String,
pub description: Option<String>, // specific to the torrent

View File

@@ -13,7 +13,7 @@ pub async fn create_artist(
current_user: &User,
) -> Result<Artist, Box<dyn Error>> {
let create_artist_query = r#"
INSERT INTO artists (name, description, pictures, created_by)
INSERT INTO artists (name, description, pictures, created_by_id)
VALUES ($1, $2, $3, $4)
RETURNING *;
"#;

View File

@@ -12,7 +12,7 @@ pub async fn create_edition_group(
current_user: &User,
) -> Result<EditionGroup, Box<dyn Error>> {
let create_title_group_query = r#"
INSERT INTO edition_groups (title_group, name, release_date, created_by, description, distributor, covers, external_links, language, source)
INSERT INTO edition_groups (title_group_id, name, release_date, created_by_id, description, distributor, covers, external_links, language, source)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::source_enum)
RETURNING *;
"#;

View File

@@ -12,7 +12,7 @@ pub async fn create_master_group(
current_user: &User,
) -> Result<MasterGroup, Box<dyn Error>> {
let create_master_group_query = r#"
INSERT INTO master_groups (name,created_by)
INSERT INTO master_groups (name,created_by_id)
VALUES ($1, $2)
RETURNING *;
"#;

View File

@@ -1,13 +1,11 @@
use std::error::Error;
use actix_web::web;
use sqlx::PgPool;
use crate::models::{
title_group::{TitleGroup, UserCreatedTitleGroup},
user::User,
};
use actix_web::web;
use serde_json::Value;
use sqlx::{PgPool, postgres::PgRow, types::JsonRawValue};
use std::error::Error;
pub async fn create_title_group(
pool: &web::Data<PgPool>,
@@ -15,8 +13,8 @@ pub async fn create_title_group(
current_user: &User,
) -> Result<TitleGroup, Box<dyn Error>> {
let create_title_group_query = r#"
INSERT INTO title_groups (master_group,name,name_aliases,created_by,description,original_language,country_from,covers,external_links,embedded_links,category,content_type,original_release_date,tags,tagline)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12::content_type_enum, $13, $14, $15)
INSERT INTO title_groups (master_group_id,name,name_aliases,created_by_id,description,original_language,country_from,covers,external_links,embedded_links,category,content_type,original_release_date,tags,tagline)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11::category_enum, $12::content_type_enum, $13, $14, $15)
RETURNING *;
"#;
@@ -52,3 +50,50 @@ pub async fn create_title_group(
}
}
}
pub async fn find_title_group(
pool: &web::Data<PgPool>,
title_group_id: i32,
) -> Result<Value, Box<dyn Error>> {
let title_group = sqlx::query!(r#"WITH torrent_data AS (
SELECT
t.edition_group_id,
jsonb_agg(
to_jsonb(t) || jsonb_build_object('uploader', jsonb_build_object('id', u.id, 'username', u.username))
) AS torrents
FROM torrents t
LEFT JOIN users u ON u.id = t.created_by_id
GROUP BY t.edition_group_id
),
edition_data AS (
SELECT
eg.title_group_id,
jsonb_agg(
to_jsonb(eg) || jsonb_build_object('torrents', COALESCE(td.torrents, '[]'::jsonb))
) AS edition_groups
FROM edition_groups eg
LEFT JOIN torrent_data td ON td.edition_group_id = eg.id
GROUP BY eg.title_group_id
)
SELECT jsonb_build_object(
'title_group', to_jsonb(tg),
'edition_groups', COALESCE(ed.edition_groups, '[]'::jsonb)
)
FROM title_groups tg
LEFT JOIN edition_data ed ON ed.title_group_id = tg.id
WHERE tg.id = $1"#, title_group_id)
.fetch_one(pool.get_ref())
.await;
match title_group {
Ok(_) => Ok(title_group.unwrap().jsonb_build_object.unwrap()),
Err(e) => {
println!("{:#?}", e);
match e {
sqlx::Error::Database(db_error) => db_error.message().to_string(),
_ => e.to_string(),
};
Err(format!("could not find title group").into())
}
}
}

View File

@@ -16,7 +16,7 @@ pub async fn create_torrent(
) -> Result<Torrent, Box<dyn Error>> {
let create_torrent_query = r#"
INSERT INTO torrents (
edition_group, created_by, release_name,
edition_group_id, created_by_id, release_name,
release_group, description, file_amount_per_type, uploaded_as_anonymous,
file_list, mediainfo, trumpable, staff_checked, size,
duration, audio_codec, audio_bitrate, audio_bitrate_sampling,

View File

@@ -6,7 +6,7 @@ use crate::handlers::{
edition_group_handler::add_edition_group,
invitation_handler::send_invitation,
master_group_handler::add_master_group,
title_group_handler::add_title_group,
title_group_handler::{add_title_group, get_title_group},
torrent_handler::upload_torrent,
};
@@ -21,6 +21,7 @@ pub fn init(cfg: &mut web::ServiceConfig) {
// .route("/torrent", web::post().to(upload_torrent))
.route("/master-group", web::post().to(add_master_group))
.route("/title-group", web::post().to(add_title_group))
.route("/title-group", web::get().to(get_title_group))
.route("/edition-group", web::post().to(add_edition_group))
.route("/torrent", web::post().to(upload_torrent))
.route("/artist", web::post().to(add_artist))