Initial commit
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
This commit is contained in:
commit
a5649447ff
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
.idea/
|
1395
Cargo.lock
generated
Normal file
1395
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "moviequotebot_api"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["louis"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "4.0.0-beta.6"
|
||||||
|
serde = "1.0.126"
|
||||||
|
serde_json = "1.0.64"
|
||||||
|
diesel = { version = "1.4.7", features = ["postgres", "chrono"] }
|
||||||
|
chrono = { version = "0.4.19", features = ["serde"]}
|
5
diesel.toml
Normal file
5
diesel.toml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# For documentation on how to configure this file,
|
||||||
|
# see diesel.rs/guides/configuring-diesel-cli
|
||||||
|
|
||||||
|
[print_schema]
|
||||||
|
file = "src/schema.rs"
|
0
migrations/.gitkeep
Normal file
0
migrations/.gitkeep
Normal file
2
migrations/2021-06-08-102332_create_subtitle/down.sql
Normal file
2
migrations/2021-06-08-102332_create_subtitle/down.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
DROP TABLE IF EXISTS properties, films, languages, subtitle_lines, subtitles, user_config;
|
93
migrations/2021-06-08-102332_create_subtitle/up.sql
Normal file
93
migrations/2021-06-08-102332_create_subtitle/up.sql
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
-- Your SQL goes here
|
||||||
|
/**
|
||||||
|
Store some information on the application.
|
||||||
|
*/
|
||||||
|
CREATE TABLE IF NOT EXISTS properties
|
||||||
|
(
|
||||||
|
id int GENERATED ALWAYS AS IDENTITY,
|
||||||
|
app_key text NOT NULL,
|
||||||
|
app_value text NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE (app_key)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Film
|
||||||
|
*/
|
||||||
|
CREATE TABLE IF NOT EXISTS films
|
||||||
|
(
|
||||||
|
id int GENERATED ALWAYS AS IDENTITY,
|
||||||
|
imdb_id varchar(10) NOT NULL,
|
||||||
|
title text NOT NULL,
|
||||||
|
year int,
|
||||||
|
film_type text NOT NULL,
|
||||||
|
season int,
|
||||||
|
episode int,
|
||||||
|
poster_link text,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE (imdb_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Available languages
|
||||||
|
*/
|
||||||
|
CREATE TABLE IF NOT EXISTS languages
|
||||||
|
(
|
||||||
|
id int GENERATED ALWAYS AS IDENTITY,
|
||||||
|
alpha3_b char(3) NOT NULL,
|
||||||
|
alpha3_t char(3),
|
||||||
|
alpha2 char(2),
|
||||||
|
english text NOT NULL,
|
||||||
|
french text NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE (alpha3_b)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Subtitles
|
||||||
|
*/
|
||||||
|
CREATE TABLE IF NOT EXISTS subtitles
|
||||||
|
(
|
||||||
|
id int GENERATED ALWAYS AS IDENTITY,
|
||||||
|
film_id int NOT NULL,
|
||||||
|
language_id int NOT NULL,
|
||||||
|
importer_id bigint,
|
||||||
|
importer_guild_id bigint,
|
||||||
|
imported_date timestamptz NOT NULL DEFAULT now(),
|
||||||
|
UNIQUE (film_id, language_id),
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY (film_id)
|
||||||
|
REFERENCES films (id),
|
||||||
|
FOREIGN KEY (language_id)
|
||||||
|
REFERENCES languages (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Subtitle lines
|
||||||
|
*/
|
||||||
|
CREATE TABLE IF NOT EXISTS subtitle_lines
|
||||||
|
(
|
||||||
|
id int GENERATED ALWAYS AS IDENTITY,
|
||||||
|
subtitle_id int NOT NULL,
|
||||||
|
dialog_line text NOT NULL,
|
||||||
|
time_code text NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY (subtitle_id)
|
||||||
|
REFERENCES subtitles (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
User configurations
|
||||||
|
*/
|
||||||
|
CREATE TABLE IF NOT EXISTS user_config
|
||||||
|
(
|
||||||
|
id int GENERATED ALWAYS AS IDENTITY,
|
||||||
|
user_id bigint NOT NULL,
|
||||||
|
guild_id bigint DEFAULT NULL,
|
||||||
|
default_language_id int DEFAULT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
UNIQUE (user_id, guild_id),
|
||||||
|
FOREIGN KEY (default_language_id)
|
||||||
|
REFERENCES languages (id)
|
||||||
|
);
|
||||||
|
|
8
src/lib.rs
Normal file
8
src/lib.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use diesel::pg::PgConnection;
|
||||||
|
use std::env;
|
||||||
|
use diesel::Connection;
|
||||||
|
|
||||||
|
pub async fn establish_connection() -> PgConnection {
|
||||||
|
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||||
|
PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
|
||||||
|
}
|
32
src/main.rs
Normal file
32
src/main.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate diesel;
|
||||||
|
extern crate actix_web;
|
||||||
|
|
||||||
|
mod schema;
|
||||||
|
mod model;
|
||||||
|
mod route;
|
||||||
|
mod lib;
|
||||||
|
|
||||||
|
use actix_web::{App, HttpServer, Responder, web};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
|
||||||
|
async fn health() -> impl Responder {
|
||||||
|
"Healthy"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
HttpServer::new(|| {
|
||||||
|
App::new().service(web::scope("/v1")
|
||||||
|
.configure(route::init_routes)
|
||||||
|
)
|
||||||
|
.route("/health", web::get().to(health))
|
||||||
|
})
|
||||||
|
.bind(format!("{}:{}",
|
||||||
|
env::var("HOST_IP").unwrap_or("0.0.0.0".to_string()),
|
||||||
|
env::var("HOST_PORT").unwrap_or("8080".to_string())
|
||||||
|
))?
|
||||||
|
.run()
|
||||||
|
.await
|
||||||
|
}
|
60
src/model.rs
Normal file
60
src/model.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Queryable)]
|
||||||
|
pub struct Subtitle {
|
||||||
|
pub id: i32,
|
||||||
|
pub film_id: i32,
|
||||||
|
pub language_id: i32,
|
||||||
|
pub importer_id: Option<i64>,
|
||||||
|
pub importer_guild_id: Option<i64>,
|
||||||
|
pub imported_date: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Queryable)]
|
||||||
|
pub struct Language {
|
||||||
|
pub id: i32,
|
||||||
|
pub alpha3_b: String,
|
||||||
|
pub alpha3_t: Option<String>,
|
||||||
|
pub alpha2_b: Option<String>,
|
||||||
|
pub english: String,
|
||||||
|
pub french: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Queryable)]
|
||||||
|
pub struct Film {
|
||||||
|
pub id: i32,
|
||||||
|
pub imdb_id: String,
|
||||||
|
pub title: String,
|
||||||
|
pub year: Option<i32>,
|
||||||
|
pub film_type: String,
|
||||||
|
pub season: Option<i32>,
|
||||||
|
pub episode: Option<i32>,
|
||||||
|
pub poster_link: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct SubtitleTrimmed {
|
||||||
|
pub id: i32,
|
||||||
|
pub film_id: i32,
|
||||||
|
pub language_id: i32,
|
||||||
|
pub imported_date: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubtitleTrimmed {
|
||||||
|
pub fn from(s: Subtitle) -> SubtitleTrimmed {
|
||||||
|
SubtitleTrimmed {
|
||||||
|
id: s.id,
|
||||||
|
film_id: s.film_id,
|
||||||
|
language_id: s.language_id,
|
||||||
|
imported_date: s.imported_date
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Queryable)]
|
||||||
|
pub struct SubtitleOutV1 {
|
||||||
|
pub subtitles: Vec<SubtitleTrimmed>,
|
||||||
|
pub films: Vec<Film>,
|
||||||
|
pub languages: Vec<Language>,
|
||||||
|
}
|
44
src/route.rs
Normal file
44
src/route.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
extern crate diesel;
|
||||||
|
|
||||||
|
use actix_web::{get, web, HttpResponse, Responder};
|
||||||
|
use crate::diesel::prelude::*;
|
||||||
|
|
||||||
|
use crate::lib::establish_connection;
|
||||||
|
use crate::model::*;
|
||||||
|
|
||||||
|
#[get("/subtitles/list")]
|
||||||
|
async fn list_all() -> impl Responder {
|
||||||
|
use crate::schema::films::dsl::*;
|
||||||
|
use crate::schema::subtitles::dsl::*;
|
||||||
|
use crate::schema::languages::dsl::*;
|
||||||
|
|
||||||
|
let connection = establish_connection().await;
|
||||||
|
let results = subtitles
|
||||||
|
.inner_join(films)
|
||||||
|
.inner_join(languages)
|
||||||
|
.load::<(Subtitle, Film, Language)>(&connection).expect("Error loading subtitles");
|
||||||
|
let mut s: Vec<SubtitleTrimmed> = vec![];
|
||||||
|
let mut f: Vec<Film> = vec![];
|
||||||
|
let mut l: Vec<Language> = vec![];
|
||||||
|
for r in results {
|
||||||
|
s.push(SubtitleTrimmed::from(r.0));
|
||||||
|
let f_id = r.1.id;
|
||||||
|
let l_id = r.2.id;
|
||||||
|
if !f.iter().any(|film| film.id == f_id) {
|
||||||
|
f.push(r.1);
|
||||||
|
}
|
||||||
|
if !l.iter().any(|language| language.id == l_id) {
|
||||||
|
l.push(r.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse::Ok().json(SubtitleOutV1{
|
||||||
|
subtitles: s,
|
||||||
|
films: f,
|
||||||
|
languages: l
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_routes(config: &mut web::ServiceConfig) {
|
||||||
|
config.service(list_all);
|
||||||
|
}
|
74
src/schema.rs
Normal file
74
src/schema.rs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
table! {
|
||||||
|
films (id) {
|
||||||
|
id -> Int4,
|
||||||
|
imdb_id -> Varchar,
|
||||||
|
title -> Text,
|
||||||
|
year -> Nullable<Int4>,
|
||||||
|
film_type -> Text,
|
||||||
|
season -> Nullable<Int4>,
|
||||||
|
episode -> Nullable<Int4>,
|
||||||
|
poster_link -> Nullable<Text>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
languages (id) {
|
||||||
|
id -> Int4,
|
||||||
|
alpha3_b -> Bpchar,
|
||||||
|
alpha3_t -> Nullable<Bpchar>,
|
||||||
|
alpha2 -> Nullable<Bpchar>,
|
||||||
|
english -> Text,
|
||||||
|
french -> Text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
properties (id) {
|
||||||
|
id -> Int4,
|
||||||
|
app_key -> Text,
|
||||||
|
app_value -> Text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
subtitle_lines (id) {
|
||||||
|
id -> Int4,
|
||||||
|
subtitle_id -> Int4,
|
||||||
|
dialog_line -> Text,
|
||||||
|
time_code -> Text,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
subtitles (id) {
|
||||||
|
id -> Int4,
|
||||||
|
film_id -> Int4,
|
||||||
|
language_id -> Int4,
|
||||||
|
importer_id -> Nullable<Int8>,
|
||||||
|
importer_guild_id -> Nullable<Int8>,
|
||||||
|
imported_date -> Timestamptz,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table! {
|
||||||
|
user_config (id) {
|
||||||
|
id -> Int4,
|
||||||
|
user_id -> Int8,
|
||||||
|
guild_id -> Nullable<Int8>,
|
||||||
|
default_language_id -> Nullable<Int4>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
joinable!(subtitle_lines -> subtitles (subtitle_id));
|
||||||
|
joinable!(subtitles -> films (film_id));
|
||||||
|
joinable!(subtitles -> languages (language_id));
|
||||||
|
joinable!(user_config -> languages (default_language_id));
|
||||||
|
|
||||||
|
allow_tables_to_appear_in_same_query!(
|
||||||
|
films,
|
||||||
|
languages,
|
||||||
|
properties,
|
||||||
|
subtitle_lines,
|
||||||
|
subtitles,
|
||||||
|
user_config,
|
||||||
|
);
|
Loading…
Reference in New Issue
Block a user